home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / pascal / pnl010.zip / PNL010.TXT < prev    next >
Text File  |  1992-03-01  |  104KB  |  2,335 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.      Issue #10                                                   March, 1992
  8.      ========================================================================
  9.            The Pascal Newsletter (C) Copyright 1992 by Alex Boisvert
  10.                                 ALL RIGHTS RESERVED
  11.  
  12.                            [][][][][]   []        []    []
  13.                           []      []   [][]      []    []
  14.                          []      []   [] []     []    []
  15.                         [][][][][]   []   []   []    []
  16.                        []           []    []  []    []
  17.                       []           []     [] []    []
  18.                      []           []      [][]    []
  19.                     []           []        []    [][][][][]
  20.  
  21.       T H E   I N T E R N A T I O N A L   P A S C A L   N E W S L E T T E R
  22.  
  23.      ========================================================================
  24.  
  25.      Table of Contents
  26.  
  27.      (*) Introduction .................................................  2
  28.               by Alex Boisvert - Editor, Writer
  29.      (*) BigFiles - A Large File Viewer................................  3
  30.               by Mich Davis - Contributing Writer
  31.      (*) BigArrays - You've Never Seen Arrays This BIG!................ 10
  32.               by Mich Davis - Contributing Writer
  33.      (*) High-Quality Sound with Turbo Pascal.......................... 13
  34.               by Alex Boisvert - Editor, Writer
  35.      (*) Overlays - How to fit your programs in memory................. 15
  36.               by Alex Boisvert - Editor, Writer
  37.      (*) Turbo Vision Without Getting GUI.............................. 19
  38.               by Richard Morris - Editor Over-the-Pond
  39.      (*) Answers to Frequently Asked Questions ........................ 26
  40.               by Trevor Carlsen - Contributing Writer
  41.      (*) Running 80286 programmes on 8088/8086 Computers............... 37
  42.               by David J. N. Begley - Contributing Writer
  43.      (*) Conclusion ................................................... 41
  44.               by Alex Boisvert - Editor, Writer
  45.  
  46.  
  47.      The Pascal Newsletter is published by Alex Boisvert as a medium for  all
  48.      Pascal programmers to share ideas,  comments,  techniques or experiences
  49.      concerning any subject related to the Pascal programming language.
  50.  
  51.      DataMAX  BBS  is  the  home  of  PNL.   It can be reached in Sherbrooke,
  52.      Quebec,  Canada at (819) 563-6327 or FidoNet @ 1:167/405.0 between 23:00
  53.      and 8:00 Eastern Standard Time.
  54.  
  55.      Articles and source code submitted by others are  the  property  of  the
  56.      authors  and  are  used  with  permission.    They  may  not  be treated
  57.      separately from this newsletter without the author's permission and thus
  58.      maintain all distribution rules of the newsletter as a whole.  It may be
  59.      freely distributed in un-modified form,  but no charge whatsoever may be
  60.      incurred on  the  recipient.    All  code  is  provided  'as-is' with no
  61.      guarantees whatsoever.
  62.  
  63.      ========================================================================
  64.       PNL #10                      Page 2                         March, 1992
  65.  
  66.  
  67.  
  68.      =================================================================
  69.                      Introduction - Editor's Note
  70.      =================================================================
  71.  
  72.      After more than a year of silence, the Myth becomes a reality.
  73.      The Pascal NewsLetter has revived.
  74.  
  75.      Many people will ask themselves why this is issue #10 and why wasn't PNL
  76.      published during the last year.
  77.  
  78.      The facts.   Pete Davis,   founder  and  ex-editor of PNL,  had personal
  79.      problems and was forced to sell his computer.  He was unable to continue
  80.      his work.
  81.  
  82.      I must thank him for the outstanding work he has  done  with  PNL.    He
  83.      worked very hard to keep the issues out and each of them was a success.
  84.  
  85.      For the sake of the Pascal community, I am taking up the baton.   I will
  86.      be  publishing the Pascal NewsLetter starting with issue #10 and up.   I
  87.      expect to  be  publishing  one  issue  bimontly,   if  I have sufficient
  88.      material (articles) to fill it.   Since  I  am  starting  from  scratch,
  89.      everything  has  to be done a second time.   I must find new columnists,
  90.      contributing writers and people to  distribute the newsletter all around
  91.      the world.   Richard Morris will be distributing it in Australia but,  I
  92.      have yet to find a distributor in Europe and, possibly, in Asia.
  93.  
  94.      As you may have noticed,  I  have  changed  a  bit  the  format  of  the
  95.      newsletter.    The table of contents now shows on the first page.   This
  96.      will be handy if you are keeping the newletters on print.
  97.  
  98.      I hope you all enjoy  the  "New  PNL"  and  I  wish that more people get
  99.      interested in it.
  100.  
  101.      Alex Boisvert
  102.      PNL's New Chief Editor.
  103.       PNL #10                      Page 3                         March, 1992
  104.  
  105.  
  106.  
  107.      =================================================================
  108.                        BigFiles - a large file viewer
  109.      =================================================================
  110.  
  111.      G'Day!  Well, it sure is good to see the Pascal NewsLetter alive and well
  112.      again.   In this article and the followup in the next edition,  we'll  be
  113.      working  on  a  very  useful  tool - a technique for accessing large text
  114.      files.
  115.  
  116.      On the way we'll be exploring a  LOT of really neat techniques all rolled
  117.      into the one program.   Unless you're a closet guru, I'm SURE you'll find
  118.      something you haven't come across yet.  We'll be dabbling in:
  119.  
  120.      o Take-only-what-you-need dynamic memory allocation.
  121.      o Vertical Scrolling.
  122.      o Turbo Pascal's Built-in Assembler.
  123.      o Using DOS to allocate memory.
  124.      o Treating TEXT files as TYPED files.
  125.      o Buffering Text files to improve access speed.
  126.      o A technique for quick access to any part of a text file.
  127.      o Objects.
  128.      o Very large arrays (bigger than 64k in size, or more than 64k
  129.        elements).
  130.      o A standalone program for viewing text files, a la Buerg's LIST.
  131.      o i286-capable programs.
  132.      o Horizontal scrolling.
  133.      o Multitasking.
  134.      o Turbo Vision.
  135.      o Optimization using Turbo Profiler.
  136.      o Optimization using the built-in assembler.
  137.      o DesqView-aware programs.
  138.  
  139.      This is all done with Turbo Pascal 6.0.   I apologise to those who  don't
  140.      have 6.0, but that's progress.
  141.  
  142.      Part 1:
  143.  
  144.      [Note: I have a 286, so I compile my programs with $G+ on.  If you don't,
  145.      remove  the  $G+  from  compiler  options,   and  the  "Test186" from the
  146.      uses-block of each program it appears in.]
  147.  
  148.      What we aim to have by the end of this article is a tool which will allow
  149.      us to view arbitrarily large text  files.   Each of the included programs
  150.      builds on the one before it, starting with MDP1.PAS.
  151.  
  152.  
  153.      The way we'll first explore is to read the lines  of  the  file  into  an
  154.      array  of strings.   MDP1.PAS is about as simple as you can get!   It has
  155.      two sections - the first section opens the file, and the second prints it
  156.      forwards and backwards.   Note that this  series of programs get the name
  157.      of the file to show from the command line,  ie MDP1 c:\autoexec.bat.   If
  158.      you forget to give it a filename,  the program will try to read from  the
  159.      keyboard.    You can exit by pressing ^Z and Enter.   Note that IDE users
  160.      can set the  command-line  parameters  by  selecting [R]un,  p[a]rameters
  161.      inside the IDE, and [R]un, [A]rguments inside Turbo Debugger.
  162.  
  163.      Note that this program is extremely limited:
  164.       PNL #10                      Page 4                         March, 1992
  165.  
  166.  
  167.  
  168.      o It is wasteful of resources to declare an array larger than we'll
  169.        need, but for many files, 100 lines won't be enough.
  170.  
  171.      o The maximum number of lines we can possibly extend this approach to,
  172.        is about 250 lines.
  173.  
  174.      For these reasons, MDP1.PAS is a bit of an evolutionary dead-end.
  175.  
  176.      MDP2.PAS is a little better.   This program works  by  reading  the  file
  177.      line-by- line, and storing it IN ONLY AS MUCH MEMORY AS IT NEEDS to store
  178.      it.    Thus,   files with (on average) shorter lines will be able to have
  179.      more lines loaded before the computer runs out of memory.
  180.  
  181.      This program introduces the idea of a  table to point to the start of the
  182.      lines in the file.  Once we have this table, it's a simple matter to look
  183.      up the line, get the address of that line in memory and print it out.
  184.  
  185.      Note the table is made of records,  with a 4-byte pointer field  for  the
  186.      start of the line, and a word for the size of the line.   We need to keep
  187.      the  size so that later we can feed the size to FreeMem - TP doesn't keep
  188.      track of how big the memory chunk it allocates you with GetMem is, so you
  189.      have to remind it at FreeMem time.
  190.  
  191.      MDP2.PAS is limited by two things -  It stores the lines from the file on
  192.      the heap,  so can only handle files of about half  a  megabyte  or  less.
  193.      Secondly,   the table of line pointers is limited to about 10000 entries,
  194.      since the address table can't  be  larger  than 64k (ie,  a little larger
  195.      than 6*10000).
  196.  
  197.      Now let's turn our attention to something different - scrolling.   Take a
  198.      look at MDP3.PAS.
  199.  
  200.      Perhaps the best way to see what it does is to run  it.    When  you  do,
  201.      you'll see a line of numbers, which you can move along by using the + and
  202.      - keys.
  203.  
  204.      This  program has two parts.   The first draws the initial screen.   Note
  205.      the call to min (minimum) in  the  For - this roughly translates to "draw
  206.      until you hit the end of the screen, or until you run out of numbers".
  207.  
  208.      The second part of the program is the standard  repeat-until/readkey/case
  209.      input  loop.    WinTop  by-the-way  is the line number which is currently
  210.      displayed at the top of the screen.
  211.  
  212.      Note that the - and +  sections  scroll the screen by using Crt's DelLine
  213.      and InsLine.   An unfortunate consequence of this is that the status line
  214.      at the bottom of the screen flickers quite  badly  when  scrolling  takes
  215.      place.  To stop this, you'd either have to put the status line at the top
  216.      of the screen, or declare a Crt.Window.  I've done neither since I didn't
  217.      find  it  all  that annoying.   The followup article in the next PNL WILL
  218.      address this problem.
  219.  
  220.      It's a good idea to step through this  program using F7 to get an idea of
  221.      just how it performs the scrolling.   Note that after the initial redraw,
  222.      only the lines that are just appearing are rewritten -  this  is  faster,
  223.      and  reduces  the inevitable screen flash (not CGA snow) that occurs when
  224.      you update a screen line-by-line.
  225.       PNL #10                      Page 5                         March, 1992
  226.  
  227.  
  228.  
  229.      Now let's have a look at MDP4.PAS.    This  is a hybrid - it combines the
  230.      file reading section from MDP1.PAS with the scroller from MDP3.PAS, so it
  231.      shouldn't contain too  many  surprises.    Note  that  in  the  scrolling
  232.      section,  the writeln's which outputted numbers have been replaced with a
  233.      reference to the data at that line position (eg, line [WinTop]).  This is
  234.      then passed to procedure PrLn,  which acts like a writeln, except that it
  235.      chops  off  lines  that  are longer than 79 characters.   If you don't do
  236.      this, a long line will overflow onto the next, spoiling the display.
  237.  
  238.      Until now,  the programs  we've  looked  at  have been fairly elementary.
  239.      Well,  you've been warned!   Programs after this point get  quite  a  bit
  240.      harder to understand, for many reasons:
  241.  
  242.      o They're more sophisticated.
  243.      o They use techniques not seen in everyday Turbo Pascal programs.
  244.      o I wrote them,  and you're having my less-than-perfect programming style
  245.        inflicted on you.  Sorry!  :-D
  246.  
  247.      Let's look at three interesting areas before we proceed to MDP5.PAS.  The
  248.      first  is  a  device  which  dramatically  speeds  up  TEXT file access -
  249.      SetTextBuf.
  250.  
  251.      SetTextBuf is part of Turbo Pascal's Run-Time Library [RTL].  It lets you
  252.      tell Turbo Pascal to use a given area of memory as a cache or buffer when
  253.      dealing with a text file.    Let's  say  we had the following fragment of
  254.      code:
  255.  
  256.      var TBuff:array [0..8191] of byte;
  257.           f:text;
  258.           line:string;
  259.  
  260.      begin
  261.        assign (f,'whatever.doc');
  262.        SetTextBuf (f,TBuff);
  263.        reset (f);
  264.        readln (f,line); readln (f,line);
  265.        close (f);
  266.      end;
  267.  
  268.      Without the SetTextBuf,  the two readlns would probably  have  cause  two
  269.      disk accesses.   Instead, the RTL will now try to fill TBuff with as much
  270.      data  from  f as it can.   Then,  requests to access the file come out of
  271.      TBuff, instead of an access to the disk.
  272.  
  273.      If you don't already,  try it in  your next program which deals with lots
  274.      of text files - you'll be pleasantly surprised at the speed increase!
  275.  
  276.      The second concept overcomes an annoying limitation with Turbo  Pascal  -
  277.      when  you're  reading  TEXT  files,  you have no way of using the FilePos
  278.      function to find out where you  are,  or the Seek procedure to reposition
  279.      yourself.  To get around this, let me introduce to you ("How do you do?")
  280.      a unit written by a fellow Australian - TextUtl2.   This  unit  lets  you
  281.      perform  FilePos,   FileSize  and Seek on text files,  and is a real life
  282.      saver!
  283.  
  284.      Third,  the idea  of  going  to  DOS  for  memory,   rather than Pascal's
  285.      GetMem/FreeMem.   Why?   Because Turbo Pascal is limited to  handing  you
  286.      blocks  of  64k at a time,  whereas DOS will happily give you a couple of
  287.       PNL #10                      Page 6                         March, 1992
  288.  
  289.  
  290.  
  291.      hundred k's of consecutive  memory,   which  we'll  be using for our mega
  292.      arrays.
  293.  
  294.      Take a look at MDP5.PAS.   The first thing to note is that now  we  don't
  295.      read  in  the  actual  data  from  the file - all we do is store the file
  296.      OFFSET of each line.   Later when we want to go to a certain line,  we'll
  297.      look up its offset,  Seek to that position, and happily read away.   This
  298.      lets us view up to 16000 lines,   which is the limit of Vern Buerg's LIST
  299.      program.
  300.  
  301.      Observe how the text buffer is set up:  TBuffSize is set to how many K we
  302.      want the buffer to be (I've picked 20k).   Then TBuffType is declared  to
  303.      be  an array of that size in K's.   Note that the buffer differs from the
  304.      code fragment we saw before,  because it'll come off the heap, instead of
  305.      being a static variable.
  306.  
  307.      Note the call to TextFilePos on line 47.   This shows just how easy it is
  308.      to use the TEXTUTL2 unit.   Before each line is read, we store the offset
  309.      for that line for future reference.
  310.  
  311.      If you're wide awake,  you'll see  that  line  48 we use a readln without
  312.      telling it where to put the data it reads.   This is  because  we're  not
  313.      interested in what's in the line, just where they start.   The reading of
  314.      the data will be done later.
  315.  
  316.      So,   the  program reads in the file,  and asks you for the starting line
  317.      number.  When you've typed a valid number in, line 64 looks up the offset
  318.      of that line,  and moves the  file  pointer to that position.   The lines
  319.      following that read the file to cover the screen, either until the bottom
  320.      of the screen is hit, or the end of the file occurs.
  321.  
  322.      Note line 71 - this is a shady trick!   If you use Crt.KeyPressed,   then
  323.      even when it detects a key it leaves it in the keyboard buffer.   So, the
  324.      program pauses at line 71 until you press a key.   When you do,  it loops
  325.      back  to  the readln in line 61,  which gets its first key free - the one
  326.      you pressed.
  327.  
  328.      This trick lets you prompt for information  in a way which isn't going to
  329.      spoil your nice screen layout until the user is ready to type something.
  330.  
  331.      Note that you only have to perform ONE TextSeek for a whole screen.   You
  332.      COULD perform a TextSeek for EVERY line on the screen,  but  why  bother?
  333.      Reading  a  line  leaves  the file pointer (and cursor!) sitting fair and
  334.      square on the next line, all ready for you to use.
  335.  
  336.      Note in line 75 we Dispose of TBuff AFTER the file has been closed.
  337.  
  338.      Now we're going to sidetrack for a moment.  Just for an experiment, we're
  339.      going to ask DOS for the memory for TBuff,  instead of TP, which uses the
  340.      heap.
  341.  
  342.      This program will be called  MDP5A.PAS  ("A" because it's an experiment).
  343.      But wait!   Do I hear you say there's  no  MDP5A.PAS  included?    You're
  344.      right.   Instead, I've put the DIFFERENCES between MDP5.PAS and MDP5A.PAS
  345.      into  a  file  called MDP5A.EDL.   This file is really a script for EDLIN
  346.      (and you thought you'd never  use  EDLIN again eh?!).   To get MDP5A.PAS,
  347.      type this at the command line:
  348.       PNL #10                      Page 7                         March, 1992
  349.  
  350.  
  351.  
  352.      C:\PNL010> edlin MDP5A.PAS < MDP5A.EDL
  353.  
  354.      ...  and this will create MDP5A.PAS.
  355.  
  356.      In fact,  I've built a Turbo Vision revision control  system  which  uses
  357.      purely  DOS  commands to do the nitty gritty work - it compares two files
  358.      with FC, and generates an Edlin script for the differences.   I'm telling
  359.      you this because it'll be in  a  future edition of the Pascal Newsletter,
  360.      so keep reading!   (And keep writing,   else  there  won't  BE  a  future
  361.      edition!)
  362.  
  363.      Note  that MDP5A.PAS uses a unit called DosMem.   I've written this as an
  364.      easy-to-use interface to  the  DOS  memory  functions.    There are three
  365.      modules:
  366.  
  367.      function Alloc(paras:word): word;
  368.      procedure Free(p:word);
  369.      function Largest: word;
  370.  
  371.      Note that each of these works with  "paragraphs",   which  on  PCs  means
  372.      "16-bytes".    Alloc  is  like  TP's  GetMem  -  it'll  give  you "paras"
  373.      paragraphs of memory,  returning with the  segment of the block.   If the
  374.      request can't be met,  it will return a segment of zero.   Free  is  like
  375.      TP's  FreeMem  -  it'll  hand  the  block  at "p" back to DOS when you've
  376.      finished with it.   Note that you  don't  have  to tell DOS how large the
  377.      block was - now if only FreeMem would do that!    Largest  is  like  TP's
  378.      MaxAvail  -  it'll tell you the largest size block you can ask for.   The
  379.      result is in paragraphs.
  380.  
  381.      Note that  the  DosMem  unit  is  written  using  the Built-in assembler.
  382.      Sorry,  pre-6.0 people.   Needle me enough and I'll write you a  .OBJ  or
  383.      some inline code which does it.
  384.  
  385.      Ok, back to business.  Line 39 of MDP5A.PAS performs the same function as
  386.      line  39  in  MDP5.PAS,  except that it asks DosMem.Alloc for the memory.
  387.      Because DosMem uses paragraphs, we have to convert the buffer size in K's
  388.      (20) to paragraphs.   We do this by multiplying by 64.   (20k * 64 = 1280
  389.      paras = 1280 * 16 = 20480 bytes = 20k).
  390.  
  391.      Note that TBuff is a pointer type, with two halves - the segment, and the
  392.      offset.   The segment comes from the result of Alloc, and the offset will
  393.      always be 0. The two are  joined  into  a  standard TP pointer by the Ptr
  394.      function.
  395.  
  396.      The other lines that have changed (57 and 75) pass the segment  of  TBuff
  397.      back to DOS - note that you do this AFTER the file is closed.
  398.  
  399.      The  reason  we  change a perfectly good program (MDP5.PAS) into one that
  400.      has to rely on DOS (!) is to give you a feel for working with DOS memory,
  401.      which is what we'll be doing in the next program.
  402.  
  403.      MDP6.PAS is the first of our programs to use the BigArray unit,  which is
  404.      covered in another PNL  article.    Basically,   the BigArray unit has an
  405.      object type called BigDOSArray,  which makes use of the >64k  ability  of
  406.      DOS's memory management to implement a VERY large array.
  407.  
  408.      We'll  be using one mega-array,  and our data type is the longint,  which
  409.      takes 4 bytes.  So, line 44 tells the array object all about it.  Line 45
  410.       PNL #10                      Page 8                         March, 1992
  411.  
  412.  
  413.  
  414.      asks the object to say how many  elements it could possibly hold,  of the
  415.      type you've told it.   In this case,  we'll be using an element for every
  416.      line,  so line 46 tells us how many  lines  our  program  is  capable  of
  417.      accessing.
  418.  
  419.      Line  47  asks the object to set up the array as LARGE as it can possibly
  420.      be made.   Note that this will consume ALL DOS memory.  If you were using
  421.      more than one of these objects, you'd probably not pass it the maximum.
  422.  
  423.      Line 55 is where things start  to get interesting.   One of BigDOSArray's
  424.      methods is called Elem.   If you pass it the  element  number,   it  will
  425.      return with a pointer to where that element is stored in memory.  So Elem
  426.      gets passed the line number,  and LinePtr then points to where we put the
  427.      offset.  Line 56 shows how the array gets its value.
  428.  
  429.      Because LinePtr holds the element's address, we could just as easily read
  430.      from  LinePtr^ as written to it,  and that's what happens in lines 73 and
  431.      74.  We find the offset of the line we want, we seek to the position, and
  432.      we read the file, just like we did in MDP5.PAS.
  433.  
  434.      One last point - note the calls  to  the Done method of LineBank in lines
  435.      66 and 86.  This hands back the memory the array consumed back to DOS.
  436.  
  437.      We now have the shell of a fairly powerful program.   On my machine,   it
  438.      says it's happy to handle over 130,000 lines (60,000 in the TP IDE).  The
  439.      fidonet  nodelist  (052)  is 16302 lines long,  and in a few weeks,  Vern
  440.      Buerg's LIST program isn't going  to  be  able to handle that.   At least
  441.      you've now got something that can!
  442.  
  443.      If you've understood everything so far,  then relax - from now on, things
  444.      are all down hill.  MDP7.PAS is just MDP6.PAS with the scrolling stuff we
  445.      worked out in MDP4.PAS.   It covers nothing new,   just  merges  the  two
  446.      techniques we developed before.
  447.  
  448.      MDP8.PAS is as far as we're going to take things this issue.  Still, it's
  449.      quite  a  program!    It's  been split up into separate procedures (as it
  450.      should have right from the  beginning!),   and  a number of features have
  451.      been added.  First, it shows you a percentage as you load the file.  This
  452.      is useful when you're loading the FidoNet nodelist,  or an online version
  453.      of the Bible [one of the best reasons to download it -  it  gives  you  a
  454.      great exerciser for your text-processing programs!  ;-D]
  455.  
  456.      Also, it now responds to extended keys, such as the arrows, and the PgUp/
  457.      PgDn/Home/End keys.  ESC has replaced 'q' for exitting, and you can press
  458.      # to jump to a certain line.   This is accomplished by having TWO CASEs -
  459.      one handles extended keys, and the other, normal files.
  460.  
  461.      I'm  the  first to admit that it's not the fastest thing around.   That's
  462.      why in the next issue of PNL we'll discuss optimising it.   We'll use the
  463.      profiler to investigate  where  the  program  is  spending  the most time
  464.      (everywhere,  ie,  equally slow!).   We'll rewrite a large chunk of it in
  465.      assembler, and we'll use multitasking to remove the annoying delay at the
  466.      start of the program.
  467.  
  468.      Also,  we'll discuss how we'd port the program to TurboVision, and how to
  469.      have it run RIGHT under DesqView.   Finally,  we'll see what  we  can  do
  470.      about  viewing  lines  that  are  longer  than  the  screen by horizontal
  471.      scrolling.
  472.       PNL #10                      Page 9                         March, 1992
  473.  
  474.  
  475.  
  476.  
  477.      Until next issue,
  478.  
  479.      Make merry with your Pascal!
  480.  
  481.         +-------------------------------------------+
  482.         | Mitch Davis                               |
  483.         | 10/1072 Whitehorse Rd.,                   |
  484.         | Box Hill, Victoria, Australia.  3128.     |
  485.         |                                           |
  486.         | Ph. +61-3-890-2062.  Fidonet: 3:634/384.6 |
  487.         +-------------------------------------------+
  488.      PNL #10                      Page 10                         March, 1992
  489.  
  490.  
  491.  
  492.      =================================================================
  493.                BigArray - you've never seen arrays this BIG!
  494.      =================================================================
  495.  
  496.      *** Error 22: Structure too large.
  497.  
  498.      *** Error 49: Data segment too large.
  499.  
  500.      *** Error 96: Too many variables.
  501.  
  502.      *** Runtime error 201 at XXXX:YYYY. {Range check}
  503.  
  504.      *** Runtime error 203 at XXXX:YYYY. {Heap full}
  505.  
  506.      How many times have you  been  frustrated by these little beauties?   How
  507.      many times has it been because you had an array that just  wouldn't  stay
  508.      down?   There are two ways to fix this - you could put your thinking on a
  509.      diet  and  work  out  some  other  way that doesn't consume quite so much
  510.      memory (recommended!) or you could do it the lazy way, and slot in a unit
  511.      called the (drum roll!) *** BigArray ***.
  512.  
  513.      BigArray will give  you  arrays  which  can  be  as  big  as  will fit in
  514.      conventional memory,  ie, they aren't limited to 64k.   The arrays can be
  515.      of any type, and are of a single dimension.  (A version being written now
  516.      lets you have arrays that spill over to disk or EMS,  as  well  as  multi
  517.      dimensionals.)
  518.  
  519.      The  large  arrays are implemented as objects.   You use the .SetElemSize
  520.      method to tell it how  big  your  elements are,  and you can subsequently
  521.      find out via .GetMaxSize how many elements  the  largest  array  can  be.
  522.      Then you call the Init method with the number of elements you'd like, and
  523.      voila, your array is done!  When you're finished with the array, call the
  524.      .Done method.
  525.  
  526.      How  are elements accessed?   You pass the element subscript to the .Elem
  527.      function, and it returns a pointer to that element.  The idea is that you
  528.      then assign  that  pointer  to  a  pointer  variable  of  the type you're
  529.      storing.   Then by deferencing the pointer (following it by a caret  [^])
  530.      you  can  then  access any fields,  bits,  WHATEVER that you would with a
  531.      plain-jane variable.
  532.  
  533.      Let's look at a sample of how you'd use them:
  534.  
  535.      program BigArrayTest;
  536.  
  537.      {Program to accompany article in issue #10 of the Pascal NewsLetter.     }
  538.      {Author: Mitch Davis, (3:634/384.6) +61-3-890-2062.                      }
  539.  
  540.      {$G+,M 16384,0,0} {Make SURE you tell TP not to steal all the DOS memory }
  541.                        {for a heap!  If you're not using dynamic variables,   }
  542.                        {set both numbers to 0.                                }
  543.  
  544.      {This program provides a nonsense demonstration of how you use the tools }
  545.      {in the BigArray unit.                                                   }
  546.  
  547.      uses Test186, BigArray; {I run my programs on a i286.  If you have an XT,}
  548.                              {Remove the "G+," from above and the "Test186"   }
  549.                              {from the uses section.                          }
  550.      PNL #10                      Page 11                         March, 1992
  551.  
  552.  
  553.  
  554.  
  555.      type PigeonType = record
  556.                          value:real;
  557.                          changed:boolean;
  558.                        end;
  559.  
  560.      var PigeonHole:BigDosArray;
  561.          PigeonPtr:^PigeonType;
  562.          PHnum, MaxSize:longint;
  563.          GlobalChanged:boolean;
  564.  
  565.      begin
  566.        writeln ('Welcome to the pigeon-hole.');
  567.        with PigeonHole do begin {This sets up the big array.}
  568.          SetElemSize (sizeof (PigeonType));
  569.            {Tells it how big each element will be}
  570.          MaxSize := GetMaxSize;
  571.          Init (MaxSize); {Make it as big as possible.
  572.                          This is not compulsory.    }
  573.        end;
  574.        writeln ('There are ',MaxSize,' pigeon-holes, numbered from 1 to '
  575.         ,MaxSize,'.');
  576.        write ('Please wait while I clear them... ');
  577.        for PHnum := 1 to MaxSize do begin
  578.          PigeonPtr := PigeonHole.Elem (PHnum); {Get the address of the element}
  579.          PigeonPtr^.Changed := false; {Reference the changed field within that}
  580.        end;                           {element.                               }
  581.        writeln ('Done.');
  582.        GlobalChanged := false;
  583.         {This will save us search time later if no changes}
  584.        repeat
  585.          write ('Which pigeon hole? (1-',MaxSize,', 0 to quit): ');
  586.          readln (PHnum);
  587.          if (PHnum > 0) and (PHnum <= MaxSize) then begin
  588.            PigeonPtr := PigeonHole.Elem (PHnum);
  589.            with PigeonPtr^ do begin
  590.              case Changed of
  591.                true :writeln ('That pigeon-hole has the value of ',Value:3:2);
  592.                false:writeln ('That is a new pigeon hole.');
  593.              end;
  594.              write ('Change it to? ');
  595.              readln (Value);
  596.              Changed := true; {means this ph will be shown in the end summary}
  597.            end;
  598.            writeln ('Pigeon-hole changed.');
  599.            GlobalChanged := true;
  600.          end;
  601.        until PHnum = 0;
  602.        writeln ('-------------------------------------------------');
  603.        case GlobalChanged of
  604.          false:writeln ('You didn''t change any pigeon holes.');
  605.          true :begin
  606.                  writeln ('The pigeon holes you changed were:');
  607.                  write ('Wait..',#13);
  608.                  for PHnum := 1 to MaxSize do begin {scan thru the ph's}
  609.                    PigeonPtr := PigeonHole.Elem (PHnum);
  610.                    with PigeonPtr^ do
  611.                      if changed then writeln (PHnum,': ',value:3:5);
  612.      PNL #10                      Page 12                         March, 1992
  613.  
  614.  
  615.  
  616.                  end;
  617.                end;
  618.        end;
  619.        writeln ('Thanks for using the pigeon-holes!');
  620.        PigeonHole.Done;
  621.      end.
  622.  
  623.      If you understand this  (in  conjunction  with the "interface" section of
  624.      the BigArray unit),  then you should have no problem working out  how  to
  625.      use it.
  626.  
  627.      A few usage notes:
  628.  
  629.         o   The   Big   Arrays   are   1-based,    that   is   if   you  call
  630.           BigDOSArray.Init(10),  then the element subscripts run from 1 up to
  631.           10.
  632.  
  633.         o There is NO checking  to  ensure  that  the  element  you  pass  to
  634.           BigDOSArray.Elem is within the defined range.   If it isn't,  then
  635.           you'll get garbage results for the pointer result.  Beware!
  636.  
  637.         o The  code for BigDOSArray hasn't been extensively tested.   Caveat
  638.           Emptor!
  639.  
  640.         o BigDOSArray  needs  optimising  BADLY!    I  plan  to  write  it in
  641.           optimised assembler throughout;  in the meantime,  you'll  have  to
  642.           sacrifice speed for convenience.
  643.  
  644.         o  I  am  aware  that Turbo Power markets code which implements large
  645.           arrays (and probably  MUCH  better  than  I've  managed here!).   I
  646.           haven't looked at their code.   Trevor Carlsen's OverSize unit also
  647.           performs a similar function.   I looked at his code,  and  couldn't
  648.           understand  it.   That's not meant as a slur on Trevor (3:690/644),
  649.           who writes code so good it  should  be framed.   I just thought I'd
  650.           take a different approach.  Upshot: If any of the other large-array
  651.           products fit your bill better, use them!
  652.  
  653.         o The BigDOSArray unit currently compiles in i286 mode.   If you have
  654.           an XT or similar, chop out the G+ and Test186 bits, in the same way
  655.           as the comments in the demo program mention.
  656.  
  657.      Well  that  about  wraps  it  up.    I'd be more than happy to answer any
  658.      queries you have about the unit.   Next issue, time and space permitting,
  659.      I'm planning on having a  followup  article which goes into optimisation,
  660.      multi-dimensional arrays, etc.  Till then?  Make merry with your Pascal!
  661.  
  662.      +-------------------------------------------+
  663.      | Mitch Davis                               |
  664.      | 10/1072 Whitehorse Rd.,                   |
  665.      | Box Hill, Victoria, Australia.  3128.     |
  666.      |                                           |
  667.      | Ph. +61-3-890-2062.  Fidonet: 3:634/384.6 |
  668.      +-------------------------------------------+
  669.      PNL #10                      Page 13                         March, 1992
  670.  
  671.  
  672.  
  673.      =================================================================
  674.            High Quality Sound in Turbo Pascal... Not Really.
  675.      =================================================================
  676.  
  677.      Many Turbo Pascal programmers find that  the  SOUND  procedure  does  not
  678.      produce  anything  interesting as far as music goes.   This is not due to
  679.      Turbo Pascal's limitation  to  use  the  speaker  but  due to the speaker
  680.      itself.   The original PC speaker is almost useless...  Unless you happen
  681.      to have the public-domain utility called RESPLAY,  written by Mark J. Cox
  682.      and (of course) Turbo Pascal.
  683.  
  684.      --- From RESPLAY v1.0 documentation: ---
  685.  
  686.      "RESPLAY is  a  memory  resident  program  designed  to  help  high-level
  687.      language programmers make the most of the PCs useless speaker.  By use of
  688.      some simple procedures, you can playback digital samples from within your
  689.      programs  and sample yourself.   Playback can either be to the PC speaker
  690.      (no too bad) or to some external hardware [...] "
  691.  
  692.       Possible uses of RESPLAY:
  693.  
  694.        o Arcade Games ("He's Dead Jim!" or realistic shooting noises)
  695.        o Sound Analysis (Fast Fourier Transforms/ analysing spoken words)
  696.        o Spelling Games for kids.
  697.        o Sampling your friends and making them sound silly (by changing
  698.          their speed, or by FFTs and altering their pitch!)
  699.        o The next 'Jive Bunny' single
  700.  
  701.      ------------------------------------------
  702.  
  703.      Resplay is fairly easy to use from Turbo Pascal because it uses interrupt
  704.      calls.   Simply fill the register set and call interrupt 2F.  If you have
  705.      any knowledge of assembler, RESPLAY will work for you!
  706.  
  707.      The original archive file of RESPLAY contains a C source code that I have
  708.      "ported" to Turbo Pascal.   I say  "ported" because the two programs work
  709.      differently with memory (loading & storing digital samples) but they both
  710.      do the same thing: Play a digital sample file.
  711.  
  712.      I have done much of the work for you.  I have created a Pascal unit which
  713.      can read and play any sample.   I have used TP's INTR procedure  to  call
  714.      the  interrupt  instead  of  direct  BASM  instructions for compatibility
  715.      purposes.   Turbo Pascal v5.5 users will be able to use this code without
  716.      changing one line of code.
  717.  
  718.      The unit is name DIGISND.PAS and the program is PLAYDIGI.PAS.
  719.  
  720.      I must admit that  the  program  is  not  fully optimized.   It reads the
  721.      sample file by allocating 64k memory segments and  plays  these  segments
  722.      one after the other...  I have played samples up to 350k with it.  If the
  723.      sample  is  bigger  than  64k,  you will notice some delay (under 10 ms.)
  724.      between each 64k "chunck".   It is  possible  to tell RESPLAY to play the
  725.      whole sample if the sample is stored continuously  in  memory.    I  have
  726.      decided to play the game safely and play the samples by 64k segments.
  727.  
  728.      The code is object-oriented so that anyone wishing to modify it may do so
  729.      by creating a descendant object type.  Someone might want to add a method
  730.      to play only part of the sample read (eg.   from time x to time y),  play
  731.      PNL #10                      Page 14                         March, 1992
  732.  
  733.  
  734.  
  735.      the sample on a COVOX card instead of the PC speaker, etc...
  736.  
  737.      Please refer to RESPLAY1.ZIP before modifying the unit.
  738.  
  739.      [Editor's  Note:   RESPLAY1.ZIP  has  been  included  in the distribution
  740.      archive of PNL#10.   It contains the original documentation and a digital
  741.      sample.]
  742.  
  743.      By the way, I have tested RESPLAY with my program on a COVOX card and the
  744.      resulting "music" is better than  with  a SoundBlaster card.   Also,  try
  745.      getting Macintosh (Yes,  Macintosh!) digital samples, they work just fine
  746.      with RESPLAY.
  747.  
  748.      Hope you have fun with your PC speaker now!
  749.  
  750.      Alex Boisvert
  751.      DataMAX Communications Enr.
  752.      FidoNet @ 1:167/405
  753.      PNL #10                      Page 15                         March, 1992
  754.  
  755.  
  756.  
  757.      =================================================================
  758.              Overlays - How to fit your programs in memory
  759.      =================================================================
  760.  
  761.      Overlays in Turbo Pascal help you, the programmer, to write programs that
  762.      otherwise would not fit into the 640k limitation of DOS.   They can also,
  763.      in special cases,  reduce your code's memory requierement in order to get
  764.      more memory for dynamic allocations.
  765.  
  766.      To accomplish this, overlays are "parts" of your programs that are stored
  767.      on disk and which are controlled  by  the program,  meaning that they are
  768.      loaded in memory only when they are needed.  The principle is to allocate
  769.      the same (limited) memory space to several sections  of  program...    at
  770.      different time obviously.
  771.  
  772.      The  major drawback of using overlays is the time involved in loading the
  773.      useful sections at run time,   or  "on-the-fly".   You will therefore use
  774.      this method only when available memory is less than your program's  size.
  775.      Your  programs  will then become independent of RAM memory available,  to
  776.      which the computer has access to.
  777.  
  778.      The developpers' of Turbo Pascal have kept to a minimum the rules to obey
  779.      when using the  overlays.    Management  of  overlays  is  handled by the
  780.      routines of the OVERLAY unit,  which you will have to add  to  your  USES
  781.      statement at the beginning of your main program.
  782.  
  783.      The  smallest  portion  of code which can be declared as "overlayed" is a
  784.      unit.   With  Turbo  Pascal,   it  is  not  possible  to overlay specific
  785.      routines.   Each unit is loaded in the heap memory zone,   which  is  the
  786.      reserved memory between the stack and the memory of dynamically allocated
  787.      variables.
  788.  
  789.      When using overlays, you must declare all your program's routines as FAR.
  790.      The easiest way to do this is to add the following compiler directive
  791.  
  792.        {$F+},
  793.  
  794.      just after your program's header.  Any unit compied with the directive
  795.  
  796.        {$O+},
  797.  
  798.      can then be compiled as an overlayed unit, but they do not have to.  This
  799.      directive  will  tell  the compiler to produce the needed overlay control
  800.      code.
  801.  
  802.      The following compiler directive
  803.  
  804.        {$O UNIT_NAME}
  805.  
  806.      will indicate if a unit specified in the USES statement will, in fact, be
  807.      overlayed.    It  is  therefore   possible   to  use  this  unit  without
  808.      "overlaying" it simply by using the USES statement and ommiting  the  {$O
  809.      UNIT_NAME} directive.  This unit would then be treated "normally", as any
  810.      other unit declared by the USES statement.
  811.  
  812.      This  represents  an  alternative.    You  can overlay the unit or not...
  813.      depending on the program using it.
  814.      PNL #10                      Page 16                         March, 1992
  815.  
  816.  
  817.  
  818.      When you use the overlay technique,  you have to specify the OVERLAY unit
  819.      in the USES statement and, initialize overlay management by calling the
  820.  
  821.        OVRINIT('filename.OVR'),
  822.  
  823.      routine.   It is best  if  this  is  done  right  at the beginning of the
  824.      program.  Here is an example:
  825.  
  826.         PROGRAM myprogram;
  827.         {$F+}   {forces FAR calls}
  828.         USES
  829.           OVERLAY, CRT,  {TP 6 units}
  830.           myunit1, myunit2, myunit3;   {your own units}
  831.  
  832.         {$O myunit1}
  833.         {$O myunit2}  {Overlay these two units ... }
  834.                       {Note that "myunit3" is overlayed.}
  835.  
  836.         {...}
  837.         {declaration goes here}
  838.         {...}
  839.  
  840.         BEGIN {main program}
  841.           OVRINIT('MYPROG.OVR');
  842.           {...}
  843.           {other commands here}
  844.           {...}
  845.         END.
  846.  
  847.      In this case,  the compiler creates two files: MYPROG.EXE, the executable
  848.      file, and MYPROG.OVR, the overlay file.
  849.  
  850.      Now,  the following  example  will  demonstrate  the  basics  of  overlay
  851.      management.   The program will integrate two units,  OVER1 and OVER2, and
  852.      call  two  routines  that  they  contain:  HELLO1 and HELLO2.   These two
  853.      procedures will simply display a message.
  854.  
  855.      Here are the two overlays:
  856.  
  857.         Unit OVER1;  {OVER1.PAS}
  858.         {$F+,O+}
  859.  
  860.         Interface
  861.           procedure HELLO1;
  862.  
  863.         Implementation
  864.         uses
  865.           crt;  {TP unit}
  866.  
  867.         procedure HELLO1;
  868.         begin
  869.           writeln('Hello from unit OVER1.');
  870.           delay(1000);
  871.         end;
  872.  
  873.         end. {end of unit OVER1}
  874.      PNL #10                      Page 17                         March, 1992
  875.  
  876.  
  877.  
  878.         Unit OVER2;  {OVER2.PAS}
  879.         {$F+,O+}
  880.  
  881.         Interface
  882.           procedure HELLO2;
  883.  
  884.         Implementation
  885.         uses
  886.           crt;  {TP unit}
  887.  
  888.         procedure HELLO2;
  889.         begin
  890.           writeln('Hello from unit OVER2.');
  891.           delay(1000);
  892.         end;
  893.  
  894.         end. {end of unit OVER2}
  895.  
  896.      Now,  compile the sources of the two overlays.   Once this is done,  your
  897.      disk will contain two source files OVER1.PAS and OVER2.PAS and also their
  898.      corresponding compiled TPU unit OVER1.TPU  and OVER2.TPU.   Do not forget
  899.      to select the option "Compile/Destination" to Disk,  otherwise,  the  two
  900.      TPU files will not be created.
  901.  
  902.      The main program looks like this:
  903.  
  904.         program OverlayDemo; {OVRDEMO.PAS}
  905.         {$F+}
  906.  
  907.         uses
  908.           overlay, crt,    {TP units}
  909.           over1, over2;    {overlays}
  910.  
  911.         {$O over1}
  912.         {$O over2}
  913.  
  914.         var
  915.           i: integer; {loop counter}
  916.  
  917.         begin  {main program}
  918.           ovrinit('ovrdemo.ovr');  {initialization}
  919.           for i:= 1 to 10 do
  920.           begin
  921.             clrscr;
  922.             Hello1;  {call routine in unit OVER1}
  923.             Hello2;  {call routine in unit OVER2}
  924.           end;
  925.           write('Program is finished.  Please press [ENTER].');
  926.           readln;
  927.         end. {program}
  928.  
  929.      The  two  units  merged  together  to  form  the overlay OVRDEMO.OVR when
  930.      compiling the  program.    Compiling  will  also  (obviously)  create the
  931.      executable OVRDEMO.EXE.
  932.  
  933.      The overlay file  must  always  reside  in  the  same  directory  as  the
  934.      executable  file but it is also possible to merge the overlay file to the
  935.      .EXE file with the command
  936.      PNL #10                      Page 18                         March, 1992
  937.  
  938.  
  939.  
  940.  
  941.        COPY /B ovrdemo.EXE + ovrdemo.ovr
  942.  
  943.      which, in fact, appends the overlay at the end of the .EXE file.  In this
  944.      case,  the command  OvrInit('ovrdemo.EXE')  will  initialize the overlays
  945.      integrated in the .EXE file.
  946.  
  947.      As you have seen by now,  the overlays are not  difficult  to  implement.
  948.      Just  a  few modifications around the USES statements and the addition of
  949.      the compiler directives {$O ...} and {$F+} can lead to a program which is
  950.      less RAM memory dependent.   You  may  wish  to consult your Turbo Pascal
  951.      manuals for further informations  on  using  EMS  memory  with  overlays,
  952.      clearing the overlay buffer (increasing dynamically-"allocatable" memory)
  953.      and modifying its size.
  954.  
  955.      I  have  used  overlays largely with the Object Professional Library from
  956.      Turbo  Power  because  object-oriented  programs  tend  to  create larger
  957.      programs because  the  compiler  cannot  easily  "strip"  unused  virtual
  958.      objects out of the .EXE file.
  959.  
  960.      For  obviously  reasons,  another drawback of using overlays is that your
  961.      program cannot be compressed by  a  program  like PKLITE or LZEXE,  which
  962.      dynamically uncompress your program in memory when executed.  The overlay
  963.      file has to stay in its  original  uncompressed form on disk because this
  964.      is how your .EXE file expects to read it.
  965.  
  966.      Alex Boisvert
  967.      DataMAX Communications Enr.
  968.      FidoNet @ 167/405
  969.      PNL #10                      Page 19                         March, 1992
  970.  
  971.  
  972.  
  973.      =================================================================
  974.                    Turbo Vision without getting GUI.
  975.                   Copyright (c)1991 Richard A. Morris
  976.      =================================================================
  977.  
  978.  
  979.      INTRODUCTION
  980.  
  981.         Around November of 1990, as most of us know, Borland released Turbo
  982.      Pascal 6.0, and with their add-in toolbox Turbo Vision unveiled a new
  983.      Programming Methodology for Turbo Pascal called Event Driven Object
  984.      Oriented Programming.
  985.  
  986.      Well, did the Fewmets hit the Windmill or What !?!
  987.  
  988.      Erstwhile conservative commercial programmers ranted that TV was every
  989.      thing from a Goblin to a toy toolbox destined to be unsupported by
  990.      Borland, they complained that it consisted merely of cute saccharine
  991.      windows,  of little or no practical use in professional projects.  The
  992.      silence from a lot of Third-Party Code Shops, who had always provided
  993.      outstanding and timely Developer support packages, was deafening as they
  994.      waited to see how the programming community would react (Blaise being the
  995.      only exception).  In a classic Catch-22, Programmers have been warily
  996.      watching Borland to see if they will support Turbo Vision, Meanwhile
  997.      Borland, and third-party Code shops, quietly had their eyes on the Turbo
  998.      Pascal community to see if TV was to become popular enough to support,
  999.      potentially making the bitching of disgruntled users a self-fulfilling
  1000.      prophesy.
  1001.  
  1002.      It's now a year on, slowly but surely Programming journals, and Reference
  1003.      Books  are  starting to constructively cover the complexities of TV (Most
  1004.      notably Messrs.  Rubenking and Duntemann, thank you sirs).  OK so why do
  1005.      so many good Pascal programmers have so much dirt to throw at TV,  surely
  1006.      they must know what  they're  talking  about?    Well  TV is a not merely
  1007.      another toolbox full of useful  functions,   but  rather  an  application
  1008.      framework  that  impertinently  confronts  the  programmer  to completely
  1009.      rethink his/her programming design,  from scratch.   It's a big ask for a
  1010.      lot of us who have several years of training invested in our trade.  Most
  1011.      of us looked at the Demos,  and  thought 'Well I'm certainly not going to
  1012.      put my name on such a "Mickey-Mouse" program.   Wonder if I can  make  it
  1013.      look  a bit more professional?',  had a look at the code and found it too
  1014.      difficult to personalize the interface,   and  so  gave up any attempt at
  1015.      using it, and concentrated  on  our  own  custom  interfaces  that  we'd
  1016.      invested so much skull sweat upon.
  1017.  
  1018.      Two  programmers I have much respect for,  convinced me by their comments
  1019.      to take a second look at TV.   Is it worth worrying about then, well in a
  1020.      my native vernacular, 'Bloody Oath'!   I must add that it took me several
  1021.      months of painful  re-education  to  bend  my  Object Oriented programing
  1022.      abilities around TV.   Turbo Vision is certainly no beginners toolbox  as
  1023.      it  would  appear from Borland press releases and manuals,  this may have
  1024.      something to do with the sluggish move of programmers to using it.
  1025.  
  1026.      Firstly any prospective programmer  must  firmly  understand the usage of
  1027.      Pointers and heap memory,  Next Object Oriented principles must be almost
  1028.      second  nature,   Finally  the  Event  Driven  paradigm  should  be  well
  1029.      understood.   Them's three steep learning curves!   The next  problem  as
  1030.      outlined  by  Jeff  Duntemann  in  Dr  Dobbs  Journal  Nov,   1991 in his
  1031.      PNL #10                      Page 20                         March, 1992
  1032.  
  1033.  
  1034.  
  1035.      "Structured Programming" column (Read it!), is that there no "Front Door"
  1036.      to start off your Research,  that you must digest Turbo Vision in a whole
  1037.      gestalt.
  1038.  
  1039.      I plan to do some articles on my experience of coming to grips with Turbo
  1040.      Vision in the next few issues of PNL, however today I'll start with a few
  1041.      nice little goodies that come with TV, which can be used independently of
  1042.      the Application Framework.   This allows us to at least deal with some of
  1043.      the TV topics without haveing to  see  the "Big Picture" at once.   Let's
  1044.      discuss  TV's  Objects  Unit,   which  contains   among   other   goodies
  1045.      Collections, and Stream.  You should have a fair grasp of Object oriented
  1046.      programming,   and  naturally  a  good concept of dynamic memory and heap
  1047.      usage.
  1048.  
  1049.  
  1050.      INTERESTING OBJECTS
  1051.  
  1052.      Lurking in the set of Turbo Vision  Units that comes free with every copy
  1053.      of Turbo Pascal 6.0 is the Unit "Objects.TPU".   It's well worth  looking
  1054.      into  this  Unit  to  see the usefull routines and objects that the Turbo
  1055.      Vision User interface uses as  it's  glue to bind itself together.   I'll
  1056.      leave Collections and Streams for last.
  1057.  
  1058.      Open the file Objects.Int (In your DOC subdirectory of a  standard  turbo
  1059.      pascal setup), and open your Turbo Vision Guide to page 190.  Refer often
  1060.      to  the  Global reference from page 327 as we talk of Types,  procedures,
  1061.      and functions,  and the  Object  Reference  from  page  205 as we talk of
  1062.      Objects.
  1063.  
  1064.  
  1065.      TYPE CONVERSION TYPES
  1066.  
  1067.      One of the main advantages and disadvantages of Pascal  has  always  been
  1068.      the  strong typing.   This means that if you have a variable of a certain
  1069.      type you can't usually  assign  it's  contents  to  a variable of another
  1070.      type.   The advantage is that it saves the unwary  programmer  writing  a
  1071.      large  variable  over a smaller variable and corrupting the memory of all
  1072.      variables  following.    The  disadvantage  is  that  it  constrains  the
  1073.      programmer from passing data between dissimilar types.
  1074.  
  1075.      Turbo pascal changed all this by allowing you to typecast between any two
  1076.      types of the same size,  for example  a  Pointer and a longint are both 4
  1077.      bytes long so we can do the following;
  1078.  
  1079.      Var
  1080.        Ptr : Pointer;
  1081.        Long: Longint;
  1082.      begin
  1083.        Long := Longint(Ptr);
  1084.      end;
  1085.  
  1086.      And the Long variable will contain a copy of the bytes of information  in
  1087.      the  Pointer  variable,   this  may not sound particularly usefull.   But
  1088.      consider the WordRec,  it allows  you  to  extract the high and low Bytes
  1089.      from a word, Visualise this example for Colours.
  1090.  
  1091.      Var
  1092.        MyColour : Word;
  1093.      PNL #10                      Page 21                         March, 1992
  1094.  
  1095.  
  1096.  
  1097.        MyBackground : Byte;
  1098.      Const
  1099.        Black = 0; White = 7;
  1100.      begin
  1101.        WordRec(MyColour).Hi := Black;
  1102.        WordRec(MyColour).Lo := White;
  1103.        MyBackground := WordRec(MyColour).Hi;
  1104.      end;
  1105.  
  1106.      Then we have the Pointer array types which  can  be  type  cast  over  a
  1107.      pointer to access the pointers contents as an array of Bytes,  or Words.
  1108.      Remember you can typecast types of the same size,  and all pointers are 4
  1109.      bytes in size regardless of the size of the block they point to.   So  in
  1110.      the following you can access the ith byte of a dynamic variable.
  1111.  
  1112.      Var
  1113.        BlockPtr : Pointer;
  1114.        Index    : Word;
  1115.      begin
  1116.        GetMem(BlockPtr,2000);
  1117.        For Index := 1 to 1000 do
  1118.          pWordArray(BlockPtr)^[Index] := Index;
  1119.      end;
  1120.  
  1121.      This  allows you to allocate a block of 1000 words (2 bytes each in size)
  1122.      numbered from 1 to 1000, each initialised with their index number.
  1123.  
  1124.  
  1125.      STRING POINTERS
  1126.  
  1127.      The  PString type,  the NewStr Function and the DisposeStr procedure are
  1128.      usefull items,  they are used  throughout TurboVision and they allow you
  1129.      to allocate and dispose of dynamic Strings easilly.  You simply pass the
  1130.      newstring function a String variable and it will return a Pointer  to  a
  1131.      copy of that string stored in Dynamic Memory.   You pass that pointer to
  1132.      DisposeStr  and it will return the Dynamic Memory used back to available
  1133.      heap memory.   Two nice tricks are that NewStr('') returns a nil pointer
  1134.      wasting no Space,  and dispose doesn't abort with an error if you dispose
  1135.      a nil pString.
  1136.  
  1137.      Sure you could implement  this  stuff  yourself,   and  most of us have.
  1138.      However it is all done  in  Objects,   and  it links in well with usefull
  1139.      routines like the String Collection.
  1140.  
  1141.      COLLECTIONS
  1142.  
  1143.      Often as programmers we want to be able to store a collection  of  items,
  1144.      we  can  use  an  array,   the  limitation  being  that  the size must be
  1145.      determined at compile time,  and can't change,  and can't be greater than
  1146.      64k.   The other option is  to  create  a linked list structure,  this is
  1147.      middling difficult to create,  but almost impossible to debug if you make
  1148.      mistakes,  and accessing an item  requires  traversal  through  preceding
  1149.      items, hardly optimised.
  1150.  
  1151.      Turbo  vision introduces an Object called (AHA!) a Collection.   It is in
  1152.      fact a  object  encapsulate  mutated  resizing  index  array  that allows
  1153.      indexed access like an array, with the resizability of a linked list, and
  1154.      because it indexes pointers to your objects you  can  store  any  dynamic
  1155.      PNL #10                      Page 22                         March, 1992
  1156.  
  1157.  
  1158.  
  1159.      variable or Object.
  1160.  
  1161.      It's  use  is  relatively  simple,   you  simply  create  a descendant of
  1162.      TCollection and overwrite it's  freeItem  method,  to tell the collection
  1163.      mechanisms how to get rid of an Item's memory,  so that for example  when
  1164.      you dispose of the whole collection it knows how to dispose of the memory
  1165.      for each item in the collection.
  1166.  
  1167.      Initialise  the  Collection  with two words,  and Insert pointers to your
  1168.      items already stored on the heap.   The two variables are used internally
  1169.      by the collection when building  the  index table,  the first variable is
  1170.      your best guess as to the starting number of items you will store and the
  1171.      second is the amount you want the table to grow by each time you add more
  1172.      variables than the current index can hold.  I generally use 10 and 10, if
  1173.      I don't know the nature of the collection.   Have a look at StrList for a
  1174.      simple example of how to store a collection of strings.
  1175.  
  1176.      If you look at my main program loop it  initialises  a  dynamic  variable
  1177.      pointing to a my new collection type, it then uses the newStr function to
  1178.      store  a  string  constant  in  the heap,  and uses the collection method
  1179.      insert(p :  Pointer)  to  insert  the  pointer  to  that  string into the
  1180.      collection.  It's a shorhand method that could be represented in longhand
  1181.      as follows;
  1182.  
  1183.        StrVar := 'Four';
  1184.        StrPtr := NewStr(StrVar);
  1185.        MyStringList^.Insert(StrPtr);
  1186.  
  1187.      Then my main loop calls a new method I created  for  this  object  called
  1188.      Print_Sentence,    which  then  calls  a  method  in  Tcollection  called
  1189.      ForEach(Action :  Pointer),  this calls a far local (Important can not be
  1190.      global) procedure pointer (PrintWord)  for  each  Item in the collection.
  1191.      Finally I dispose of my special  collection  using  the  TCollection.Done
  1192.      destructor, which calls FreeAll, which in turn Deletes the index item and
  1193.      calls  freeItem  for Each Item.   Which is why we overwrote FreeItem,  to
  1194.      show our collection how to dispose of an item.
  1195.  
  1196.      I Cheated a bit,  there is actually a StringCollection Object provided in
  1197.      Objects,  that already knows all about pStrings,  and sorts them into the
  1198.      bargain, but I thought as an example it would be good to show a little of
  1199.      the mechanism of creating a specialised collection type.
  1200.  
  1201.      But there is much more  to  collections,  the standard freeItem method is
  1202.      not just abstract,  it actually typecasts an object of type TObject  over
  1203.      the pointer to your Item,  and calls IT's virtual destructor.   Why, well
  1204.      all turbo vision objects are in fact descended from a TObject, and if you
  1205.      had inserted a TV object (Or any of your own objects descended ultimately
  1206.      from  a  TObject),   a  vanilla  TCollection  would know all about how to
  1207.      dispose of  such  an  item,   and  we  would  not  need  to  descend from
  1208.      TCollection and override TCollection.FreeItem.
  1209.  
  1210.      Unfortunately a pstring is not an object and thus can never be  descended
  1211.      from TObject,  however a TCollection IS descended from TObject,  and thus
  1212.      you could have a Collection of Collections, or (and this is the good bit)
  1213.      a  collection  of  various objects,  as long as each was descended from a
  1214.      TObject.
  1215.  
  1216.      I show an example of  a  Collection  of  Collections in the included file
  1217.      PNL #10                      Page 23                         March, 1992
  1218.  
  1219.  
  1220.  
  1221.      ReadIni.Pas, which is a simple unit to read a Windows type INI File.   It
  1222.      reads a Text file (See example Test.INI)  and  creates  a  collection  of
  1223.      TagCollections, each being a collection of parameters, ie:
  1224.  
  1225.         TAGCollection = Object(tCollection)
  1226.           |
  1227.           +- PARAMCollection = Object(tCollection)
  1228.                TAG : PString
  1229.                |
  1230.                +- PARAMItem = Object(TObject)
  1231.                     Param : pString
  1232.                     Vars  : pString;
  1233.  
  1234.      As  the  paramItem  is  a  descendant of TObject,  no special hadling is
  1235.      required  to add it to a PARAMcollection,  which has it's own TAG string,
  1236.      then all PARAMCOllections  (One  for  each  GROUP  TAG)  are  stored in a
  1237.      TAGCollection.
  1238.  
  1239.      There is sparse documentation in the Source of READINI,  To fully explain
  1240.      the code here would bulk  up  this  article,   so  I'll  leave  it  as  a
  1241.      demonstration  of  what  you  can do with collections,  and it could be a
  1242.      usefull unit as it stands without you modifying anything.
  1243.  
  1244.      STRINGLISTS
  1245.  
  1246.      The String List is another usefull object  in the OBJECTS unit.   It is a
  1247.      way of collecting a bunch of strings indexed by words.   The way it works
  1248.      is this,  you create a program to make a StringList using a tStrListMaker
  1249.      Object,  which once Constructed with an INIT,  simply requires you to use
  1250.      tStrListMaker.Put(Index,TextString) which adds TextString to  the  Object
  1251.      indexed by INDEX.  You then save it to a file (More about this in Streams
  1252.      and Resources).   Now you create another program that reads a tStringList
  1253.      from  this File,  then you simply ask the object for the text string that
  1254.      corresponds to a particular index number.
  1255.  
  1256.      The procedure is  simple,   I  have  included  the sample files Make_Err,
  1257.      Err_Desc,  and Test to describe the procedure.   You'll want to  read  on
  1258.      about  streams  and  resources as they are used in these files.   However
  1259.      I'll describe a few uses for String  Lists.   In the example I have given
  1260.      Make_Err simply puts a Text String detailing  each  Runtime  error,   and
  1261.      stores  the  description indexed with the run time error code.   Desc_Err
  1262.      simply  finds  out  the  exitcode  and   if  non-Zero  it  prints  out  a
  1263.      description.   So what you say,  well let's say for example that you have
  1264.      to create a version of your program for a swedish audience.   You  simply
  1265.      create a new ERRORS.STM file to put with your program, and you don't need
  1266.      to change and recompile your code.
  1267.  
  1268.      The  REAL  advantage  is  that now that the Europeans are finally getting
  1269.      their act together with a common  economic  bloc,  there will be a lot of
  1270.      demand for programs that are multi lingual.   If you use a stringList for
  1271.      ALL text strings in your code,  you'll be able to have users select  upon
  1272.      startup  the  language  they  wish  to converse with the program in,  and
  1273.      simply activate THAT string List.
  1274.  
  1275.      As a favour to  me  could  those  of  you  who  are fluent in Non-English
  1276.      languages,  send in to  the  Newsletter  a  modified  Make_Err  for  your
  1277.      language,  we'll compile them all together and distribute a multi lingual
  1278.      Runtime error descriptor.
  1279.      PNL #10                      Page 24                         March, 1992
  1280.  
  1281.  
  1282.  
  1283.  
  1284.      STREAMS AND RESOURCES
  1285.  
  1286.      About  the  most  sophisticated  file  handling  in Turbo pascal up until
  1287.      TP5.0,  was the  TEXT  file  type.    With  the  TP5.0 demo files Borland
  1288.      introduced a sophisticated Object called a  Stream  that  allows  you  to
  1289.      easilly  perform  polymorphic  Object  File  Input/Output.    Turbo power
  1290.      extended this with their own  stream objects in Object Professional,  and
  1291.      finally we have it  integrated  in  Turbo  Vision.    There  is  a  great
  1292.      discussion  on  Streams  in  Chapter  8 of the TV Guide,  I shan't try to
  1293.      repeat this here,  however I'll  give  a  general  run down on the use of
  1294.      Streams, and their application in the real world.  I do plan to do a more
  1295.      detailed tretise on Streams in the future (given time) based on a unit  I
  1296.      use  called  NetStrm,   which  extends  the  buffered  stream  to work on
  1297.      Networks.
  1298.  
  1299.      How does it work,  well  most  turbo  vision objects (And any Objects you
  1300.      want to use with Streams) have two extra methods,  a constructor  usually
  1301.      called Load, and a procedure called Store, which each take as a parameter
  1302.      a  stream  variable,   and  do  pretty  much what they say they do.   The
  1303.      trickiness is that  when  you  prepare  to  use  objects in streams,  you
  1304.      register the Objects you will use.
  1305.  
  1306.      Registering an Object entails sending information  to  the  Objects  unit
  1307.      information  so  that  it knows where to find your objects Load and Store
  1308.      methods,  a unique word called an ObjType, and the VMT link (Which within
  1309.      OOPS Pascal uniquely defines  an  Object  Type)  which allows the Objects
  1310.      unit to know the size of the dynamic object it must create during a  load
  1311.      construction.
  1312.  
  1313.      When an object is stored to a file, it's ObjType number is first saved to
  1314.      the Stream,  then the Store method is called to put it's information onto
  1315.      the file.   Ok So what is the VMTLink for, well when you tell a stream to
  1316.      load an object,  it reads the OBJType number,  then searches it's list of
  1317.      registered  objects  and uses the VMTlink that it finds to do all the OOP
  1318.      trickiness to construct the Object,   then  uses  the pointer to the Load
  1319.      constructor  registered  with  the  object,   to  fill  the  object  with
  1320.      information from the file.
  1321.  
  1322.      So What?   Well what this means is that your stream is asked  to  get  an
  1323.      object,  it will return to you a pointer to an object, after constructing
  1324.      it for you, WITHOUT KNOWING WHAT THE OBJECT IS.  Ha, So what again.  Well
  1325.      it  means  that you can store any registered descendant of TObject with a
  1326.      store and complimentary Load mechanism,   and  load it back again without
  1327.      having to know at compile time the information you  are  sending  to  the
  1328.      screen.
  1329.  
  1330.      Resources are simply streams with an index comprising of key strings,  in
  1331.      the  case  of  Make-Err and Desc_err I store the stringlist to a resource
  1332.      under the keystring "ERRORDESC".
  1333.  
  1334.      As an example of the use of Streams, Say you have 5 or 6 different record
  1335.      types that have to be input and  output  to a file.   In the 'Olden days'
  1336.      you would use the case command to create  a  variant  record,   and  each
  1337.      record you stored would be the size of the LARGEST record case.  This way
  1338.      you  simply declare a different Object for each record type,  and you use
  1339.      only as much space as each record  needs.   Furthermore if you have put a
  1340.      method called EditRec into an abstract Object,  and  descended  all  your
  1341.      PNL #10                      Page 25                         March, 1992
  1342.  
  1343.  
  1344.  
  1345.      record  objects  from  it,   and in each case overriden Editrec to do the
  1346.      individual record  editing  and  manipulation  you  could  simply  do the
  1347.      following;
  1348.  
  1349.       Incognito := TStream.Get;
  1350.       Incognito^.EditRec;
  1351.  
  1352.      It's a lot easier if your object is  a  Turbo  Vision  View  with  record
  1353.      information,   and editing dialogs,  but then you'd have to use the Turbo
  1354.      Vision User Interface.  Ah well, we'll all be using it sooner or later.
  1355.  
  1356.      Refference sources
  1357.  
  1358.              Turbo Vision Guide
  1359.              Borland Intl
  1360.  
  1361.              "Structured Programming"
  1362.              Dr. DOBBS journal
  1363.              November 1991
  1364.  
  1365.      About the author
  1366.  
  1367.              Richard  Morris  is  the  CEO of KHIRON Software,  a Queensland,
  1368.              Australia based Computer  consultancy,   that has been providing
  1369.              contract programmers,  and computer support to small  to  medium
  1370.              business' sin 1985.
  1371.  
  1372.              Richard can be contacted via the following
  1373.              Fidonet:  3:640/372.5
  1374.              IntlNet:  58:1100/378
  1375.              Voice:    (07) 812-3218
  1376.              Post:     C/- KHIRON Software
  1377.              P.O. Box 544,
  1378.              INDOOROOPILLY  Qld  4068.
  1379.      PNL #10                      Page 26                         March, 1992
  1380.  
  1381.  
  1382.  
  1383.      =================================================================
  1384.                 FREQUENTLY ASKED QUESTIONS IN THE PASCAL ECHO
  1385.      =================================================================
  1386.  
  1387.      Q1. How do I pass an error level code when my program finishes?
  1388.  
  1389.      A1. The halt procedure takes an optional parameter of type word.  Thus -
  1390.  
  1391.          halt(1);
  1392.  
  1393.          terminates the program with an errorlevel of 1. If halt is used
  1394.          without a parameter it is the same as -
  1395.  
  1396.          halt(0);
  1397.  
  1398.          Note:  When a program is terminated using the  halt  procedure  any
  1399.                 exit procedure that has previously been set up is executed.
  1400.  
  1401.  
  1402.      Q2. How do I empty the keyboard buffer?
  1403.  
  1404.      A2. There are several ways  that  this  can be achieved.   However the
  1405.          safest is -
  1406.  
  1407.             while Keypressed do ch := ReadKey;
  1408.  
  1409.          This requires that a variable ch of type char is  declared  and  the
  1410.          crt unit be used.  To do it without using a variable -
  1411.  
  1412.            while Keypressed do while ReadKey = #0 do;
  1413.  
  1414.          or if using TP6 with extended syntax enabled -
  1415.  
  1416.             while KeyPressed do ReadKey;
  1417.  
  1418.          If  you  do not wish to incur the substantial overhead involved with
  1419.          the use of the CRT unit and  there is no requirement for the program
  1420.          to run under a multi-tasker -
  1421.  
  1422.             var
  1423.               head : byte absolute $40:$1c;
  1424.               tail : byte absolute $40:$1e;
  1425.  
  1426.             tail := head;
  1427.  
  1428.  
  1429.      Q3.  When I redirect the screen output of my programs to a file the file
  1430.          is empty and the output still appears on the  screen.    What  am  I
  1431.          doing wrong?
  1432.  
  1433.  
  1434.      A3.    You  are  probably  using  the CRT unit and its default method of
  1435.          writing to stdout is by  direct  screen writes.   In order to enable
  1436.          output to be redirected all writes must be done by DOS.  Setting the
  1437.          variable DirectVideo to false has no effect on redirection as all it
  1438.          does is use the BIOS for screen writes - not DOS.
  1439.  
  1440.         
  1441.      PNL #10                      Page 27                         March, 1992
  1442.  
  1443.  
  1444.  
  1445.          To enable redirection you must not use the CRT unit
  1446.  
  1447.          OR
  1448.  
  1449.          assign(output,'');
  1450.          rewrite(output);
  1451.  
  1452.          This will make all output go  through DOS and thus can be redirected
  1453.          if desired.  To restore the default situation -
  1454.  
  1455.          AssignCRT(output); rewrite(output);
  1456.  
  1457.  
  1458.      Q4. How do I make a string that is lower or mixed case all uppercase?
  1459.  
  1460.      A4. There are several ways to convert lower case characters  to  upper
  1461.          case.  Here are some of them.
  1462.  
  1463.           As a procedure (excluding asm code this is the fastest way)
  1464.  
  1465.  
  1466.             procedure StrUpper(var st: string);
  1467.               var x : byte;
  1468.               begin
  1469.                 for x := 1 to length(st) do
  1470.                   st[x] := UpCase(st[x]);
  1471.               end;
  1472.  
  1473.           As a function (slower but sometimes more convenient) -
  1474.  
  1475.             function StrUpper(st: string): string;
  1476.               var x : byte;
  1477.               begin
  1478.                 StrUpper[0] := st[0];
  1479.                 for x := 1 to length(st) do
  1480.                   StrUpper[x] := UpCase(st[x]);
  1481.               end;
  1482.  
  1483.           Both  the  above  are suitable for the English language .   However
  1484.           from version 4.0 onwards,  DOS has had the facility to do this in a
  1485.           way that is country (language) specific.   I am indebted to Norbert
  1486.           Igl for the basic routine.  I have modified his code slightly.  For
  1487.           the anti-goto purists this  is  a  good  example  of a goto that is
  1488.           convenient,  efficient,  self-documenting and structured.   The dos
  1489.           calls would make this method the slowest of all.
  1490.  
  1491.  
  1492.          function StrUpper(s: string): string;
  1493.  
  1494.            { Country specific string-to-uppercase conversion. }
  1495.            { Requires DOS unit }
  1496.            label
  1497.              fail;
  1498.            var
  1499.              regs : registers;
  1500.              x    : byte;
  1501.            begin
  1502.         
  1503.      PNL #10                      Page 28                         March, 1992
  1504.  
  1505.  
  1506.  
  1507.              if lo(DosVersion) >= 4 then begin
  1508.                with regs do begin
  1509.                  ax := $6521;
  1510.                  ds := seg(s);
  1511.                  dx := ofs(s[1]);
  1512.                  cx := length(s);
  1513.                  msdos(regs);
  1514.                  if odd(flags) then { the attempted conversion failed so }
  1515.                    goto fail;
  1516.                end; { with }
  1517.              end { if DOS >= 4.0 } else
  1518.            fail:
  1519.                for x := 1 to length(s) do
  1520.                  s[x] := UpCase(s[x]);
  1521.              StrUpper := s;
  1522.            end; { StrUpper }
  1523.  
  1524.  
  1525.      Q5.    When  I include ANSI codes in a string and write that string to
  1526.          the screen the actual codes appear on the screen,  rather than the
  1527.          results they are supposed to achieve.
  1528.  
  1529.      A5.   In order for ANSI codes to be interpreted, screen writes must be
  1530.          directed through DOS and  there  must  have been a suitable driver
  1531.          loaded via the config.sys file at boot time.   All output  can  be
  1532.          directed through DOS and the driver by -
  1533.  
  1534.         Not using the crt unit
  1535.  
  1536.         OR -
  1537.  
  1538.         assign(output,'');
  1539.         rewrite(output);
  1540.  
  1541.         in which case ALL screen writes are "ANSI code sensitive"
  1542.  
  1543.         OR -
  1544.  
  1545.         You can set up write procedures that will be "ANSI code sensitive".
  1546.         (You will need an initialisation procedure to set this up.)
  1547.  
  1548.         var
  1549.           ansi : text;
  1550.  
  1551.         procedure AssignANSI(var ansifile : text);
  1552.           begin
  1553.             assign(ansifile,'CON');
  1554.             rewrite(ansifile);
  1555.           end; { AssignANSI }
  1556.  
  1557.         procedure WriteANSI(var st: string);
  1558.           begin
  1559.             write(ansi,st)
  1560.           end; { WriteANSI }
  1561.  
  1562.  
  1563.       
  1564.      PNL #10                      Page 29                         March, 1992
  1565.  
  1566.  
  1567.  
  1568.         procedure WriteLnANSI(var st: string);
  1569.           begin
  1570.             writeANSI(st);
  1571.             writeln(ansi);
  1572.           end; { WriteANSI }
  1573.  
  1574.         ObviousLy,   if  the  ANSI.SYS  driver  (or  an  equivalent) is not
  1575.         installed none of the above can work.
  1576.  
  1577.         Setting the variable DirectVideo in the  CRT unit to false will not
  1578.         achieve the desired result as this merely turns off  direct  screen
  1579.         writes and uses the BIOS for all screen output.
  1580.  
  1581.      Q6. When I try to shell to DOS nothing happens. What am I doing wrong?
  1582.  
  1583.  
  1584.      A6.    In  order to be able to execute any child process there must be
  1585.          sufficient memory available for  it  to load and execute.   Unless
  1586.          you advise differently at compile time,  a  Turbo  Pascal  program
  1587.          grabs  all  available  memory  for  itself when it is loaded.   To
  1588.          reserve  memory  for  a  child  process  use  the  compiler memory
  1589.          directive -
  1590.  
  1591.           {$M 16384,0,0)
  1592.  
  1593.         the default is -
  1594.  
  1595.           {$M 16384,0,655360}
  1596.  
  1597.         The first figure - StackMin  -  is  the  amount  of  memory  to  be
  1598.         allocated for the stack:
  1599.  
  1600.         Minimum is:    1024
  1601.         Default is:   16384
  1602.         Maximum is:   65520
  1603.  
  1604.         The  next  figure  - HeapMin -is the minumum amount of memory to be
  1605.         allocated for the heap.    If  there  is less memory available than
  1606.         this figure the program will not load.
  1607.  
  1608.         Minimum is: 0
  1609.         Default is: 0
  1610.  
  1611.         Maximum is:  655360  In practice it will  be  the  amount  of  free
  1612.                              memory  less the space required for the stack,
  1613.                              less the  code  space  of  the  program.   You
  1614.                              should set this to 0 unless your program  uses
  1615.                              the heap.   In that case, set it to the lowest
  1616.                              possible  figure  to  prevent  heap allocation
  1617.                              errors.   In most cases it is best to leave it
  1618.                              at  zero  and  do  error  checking  within the
  1619.                              program for sufficient  memory  at  allocation
  1620.                              time.
  1621.  
  1622.         The  last figure is the crucial on as regards child processes.   It
  1623.         should always be low enough to  leave  memory left over for a child
  1624.         process and high enough not to cause problems for the program  when
  1625.         allocating heap memory.
  1626.      PNL #10                      Page 30                         March, 1992
  1627.  
  1628.  
  1629.  
  1630.  
  1631.  
  1632.         Minimum is:  HeapMin
  1633.         Default is:  655360
  1634.         Maximum is:  655360     If   less  than  the  requested  amount  is
  1635.                                 available no error is reorted.  Instead all
  1636.                                 available memory is allocated for heap use.
  1637.  
  1638.  
  1639.      Q7. How do I shell to DOS?
  1640.  
  1641.      A7. SwapVectors;
  1642.          exec(GetEnv('COMSPEC','');
  1643.          SwapVectors;
  1644.  
  1645.          Read previous section on memory allocation.
  1646.  
  1647.          I find that it is a good  idea  to write my own Exec function which
  1648.          will do everything that is needed for me.   I  have  it  return  an
  1649.          integer value that is the DosError code.
  1650.  
  1651.           function Exec(p1,p2: string);
  1652.             begin
  1653.               SwapVectors;
  1654.               Dos.Exec(p1,p2);
  1655.               SwapVectors;
  1656.               Exec := DosError;
  1657.             end;
  1658.  
  1659.           This enables me to have a statement such as -
  1660.  
  1661.           ReportError(Exec(GetEnv('COMPSEC'),''));
  1662.  
  1663.           Now  you can have an empty ReportError procedure or you can make it
  1664.           report the error - whatever is suitable for you application.
  1665.  
  1666.  
  1667.      Q8. When I execute a child process redirection does not work. Why?
  1668.  
  1669.  
  1670.      A8. Redirection of a child process's  output only works if it is run
  1671.          under another copy of the command processor.  So -
  1672.  
  1673.          exec('YourProg.exe',' > nul');    will not work but
  1674.  
  1675.          exec(GetEnv('COMSPEC'),'/c YourProg > nul'); will work.
  1676.  
  1677.  
  1678.      Q9. How do I read an errorlevel from a child process?
  1679.  
  1680.      A9.   After executing a child process the errorlevel returned  can  be
  1681.          read  by  calling  the  DosExitCode function which returns a word.
  1682.          The low byte is  the  errorlevel.    A  full description is in the
  1683.          manual.
  1684.  
  1685.          If the command interpreter is the child  process  and  it  in  turn
  1686.          executes  a  child  process then the errorlevel of the second child
  1687.          process cannot be read without resorting to some trickery.
  1688.      PNL #10                      Page 31                         March, 1992
  1689.  
  1690.  
  1691.  
  1692.  
  1693.      Q10.   When I read a text file that has lines exceeding 255 characters
  1694.           I lose all those characters from the 256th one on each time there
  1695.           is a line that exceeds that length.  How can I prevent this?
  1696.  
  1697.      A10.   Turbo Pascal's readln procedure  reads  a  line up to the 255th
  1698.           character then skips to the next line.   To get around  this  you
  1699.           should declare a buffer at least as large as the longest possible
  1700.           line  and  then  use  the read procedure.   The best size for the
  1701.           buffer is a multiple of 2048 bytes.
  1702.  
  1703.         const
  1704.           BufferSize = 2048;
  1705.           LineLength = 78;
  1706.         type
  1707.           textbuffer = array[1..BufferSize] of char;
  1708.         var
  1709.           st          : string;
  1710.           f           : text;
  1711.           buffer      : textbuffer;
  1712.  
  1713.         function ReadTxtLn(var tf: text; var s: string; max: byte): integer;
  1714.           { Reads a string of a maximum length from a text file }
  1715.           var
  1716.             len         : byte absolute s;
  1717.           begin
  1718.             len := 0;
  1719.             {$I-}
  1720.             while (len < max) and not eoln(tf) do begin
  1721.               inc(len);
  1722.               read(tf);
  1723.             end;
  1724.             if eoln(tf) then
  1725.               readln(tf);
  1726.             ReadTxtLn := IOResult;
  1727.             {$I+}
  1728.           end; { ReadTxtLn }
  1729.  
  1730.         begin
  1731.           assign(f,filename);
  1732.           reset(f);
  1733.           SetTextBuf(f,buffer);
  1734.           while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) do
  1735.             writeln(st);
  1736.           close(f);
  1737.         end.
  1738.  
  1739.  
  1740.      Q11.   How do I convert nul  terminated asciiz strings to Turbo Pascal
  1741.           strings?
  1742.  
  1743.      A11.  Here is a function that will do that -
  1744.  
  1745.         function Asc2Str(var s; max: byte): string;
  1746.           { Converts an ASCIIZ string to a Turbo Pascal string }
  1747.           { with a maximum length of max.                      }
  1748.           var starray  : array[1..255] of char absolute s;
  1749.               len      : integer;
  1750.      PNL #10                      Page 32                         March, 1992
  1751.  
  1752.  
  1753.  
  1754.           begin
  1755.             len        := pos(#0,starray)-1;              { Get the length }
  1756.             if (len > max) or (len < 0) then      { length exceeds maximum }
  1757.               len      := max;                         { so set to maximum }
  1758.             Asc2Str    := starray;
  1759.             Asc2Str[0] := chr(len);                           { Set length }
  1760.           end;  { Asc2Str }
  1761.  
  1762.  
  1763.      Q12.   How can I tell if a  particular  bit  of a variable is set or not?
  1764.           How can I set it?   How can I turn it off?   How can I make a  large
  1765.           bit  map  and  then determine if a particular bit - say bit 10000 is
  1766.           on/of?
  1767.  
  1768.      A12.   This question,  or a variation of it,  is one of the most commonly
  1769.           asked questions in the echo and there are several ways of doing what
  1770.           is wanted.   None are necessarily  right  or wrong.   The way I will
  1771.           describe is designed  to  take  up  as  little  code/data  space  as
  1772.           possible.    I  do  not  attempt  to explain the theory behind these
  1773.           functions as this can be obtained from any good book.
  1774.  
  1775.         The use of sets can be  the  best  bit manipulation method if you have
  1776.         control over the data being used.   Here  is  an  example  of  a  byte
  1777.         variable for a BBS program which sets various user access level flags.
  1778.  
  1779.            Bit 0 = Registered User
  1780.                1 = Twit
  1781.                2 = Normal
  1782.                3 = Extra
  1783.                4 = Privileged
  1784.                5 = Visiting Sysop
  1785.                6 = Assistant Sysop
  1786.                7 = Sysop
  1787.  
  1788.          type
  1789.            status_type  = (Registered,
  1790.                            Twit,
  1791.                            Normal,
  1792.                            Extra,
  1793.                            Privileged,
  1794.                            VisitingSysop,
  1795.                            AssistantSysop,
  1796.                            Sysop);
  1797.             status_level = set of status_type;
  1798.  
  1799.          var
  1800.            access_flags  : status_level;
  1801.  
  1802.         Let  us  assume you have someone who logs on and you wish to determine
  1803.         his user access level.   After reading access_flags from the user data
  1804.         file -
  1805.  
  1806.              if Sysop in access_flags then ....
  1807.  
  1808.         To set the sysop flag -
  1809.  
  1810.              access_flags := access_flags + [Sysop];
  1811.      PNL #10                      Page 33                         March, 1992
  1812.  
  1813.  
  1814.  
  1815.         To reset the sysop flag -
  1816.  
  1817.              access_flags := access_flags - [Sysop];
  1818.  
  1819.         However on many occasions using  a  set  may not be a suitable method.
  1820.         You may simply need to know if bit 5 is set  or  not.    Here  is  the
  1821.         method that I consider the best -
  1822.  
  1823.           function BitIsSet(var V,  bit: byte): boolean;
  1824.             begin
  1825.               BitIsSet := odd(V shr bit);
  1826.             end;
  1827.  
  1828.         To set a bit -
  1829.  
  1830.            procedure SetBit(var V: byte; bit: byte);
  1831.              begin
  1832.                V := V or (1 shl bit);
  1833.              end;
  1834.  
  1835.         To reset a bit -
  1836.  
  1837.            procedure ResetBit(var V: byte; bit: byte);
  1838.              begin
  1839.                V := V and not(1 shl bit);
  1840.              end;
  1841.  
  1842.         To toggle (flip) a bit -
  1843.  
  1844.            procedure ToggleBit(var V: byte; bit: byte);
  1845.              begin
  1846.                V := V xor (1 shl bit);
  1847.              end;
  1848.  
  1849.         Now a bit map can be made up from an array of bytes.   If stored on
  1850.         the  heap  you  can  test any bit up to number 524159 (zero based).
  1851.         Here's how.
  1852.  
  1853.         type
  1854.           map = array[0..maxsize] of byte;
  1855.           { set maxsize to number of bits div 8 -1 needed in the bit map }
  1856.  
  1857.         function BitSetInBitMap(var x; numb : longint): boolean;
  1858.           { Tests the numb bit in the bitmap array }
  1859.           var m: map absolute x;
  1860.           begin
  1861.             BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7));
  1862.           end;
  1863.  
  1864.         procedure SetBitInBitMap(var x; numb: word);
  1865.           { Sets the numb bit in the bitmap array }
  1866.           var m: map absolute x;
  1867.           begin
  1868.             m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7))
  1869.           end;
  1870.  
  1871.         procedure ResetBitInBitMap(var x; numb : longint);
  1872.           { Resets the numb bit in the bitmap array }
  1873.      PNL #10                      Page 34                         March, 1992
  1874.  
  1875.  
  1876.  
  1877.           var m: map absolute x;
  1878.           begin
  1879.            m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7));
  1880.           end;
  1881.  
  1882.         procedure ToggleBitInBitMap(var x; numb : longint);
  1883.           { Toggles (flips) the numb bit in the bitmap array }
  1884.           var m: map absolute x;
  1885.           begin
  1886.             m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7));
  1887.           end;
  1888.  
  1889.  
  1890.      Q13. How can I find a particular string in any file - text or binary?
  1891.  
  1892.      A13.    The  Boyer-Moore  string search algorithm is considered to be the
  1893.           fastest method available.   However in a rare worst-case scenario it
  1894.           can be  slightly  slower  than  a  linear  brute-force method.   The
  1895.           following demonstration program will show how  it  works  and  could
  1896.           easily be modified to allow for command line paramters etc.
  1897.  
  1898.  
  1899.         program BMSearchDemo;
  1900.  
  1901.         type
  1902.           bigarray = array[0..32767] of byte;
  1903.           baptr    = ^bigarray;
  1904.           BMTable  = array[0..255] of byte;
  1905.  
  1906.         const
  1907.           KeyStr : string = 'Put whatever you want found here';
  1908.           fname  : string = 'f:\Filename.txt';
  1909.  
  1910.         var
  1911.           Btable : BMtable;
  1912.           buffer : baptr;
  1913.           f      : file;
  1914.           result,
  1915.           position : word;
  1916.           offset : longint;
  1917.           finished,
  1918.           Strfound  : boolean;
  1919.  
  1920.         procedure MakeBMTable(var t : BMtable; var s);
  1921.           { Makes a Boyer-Moore search table. s = the search string}
  1922.           { t = the table }
  1923.           var
  1924.             st  : BMtable absolute s;
  1925.             slen: byte absolute s;
  1926.             x   : byte;
  1927.           begin
  1928.             FillChar(t,sizeof(t),slen);
  1929.             for x := slen downto 1 do
  1930.               if (t[st[x]] = slen) then
  1931.                 t[st[x]] := slen - x
  1932.           end;
  1933.  
  1934.         function BMSearch(var buff,st; size : word): word;
  1935.      PNL #10                      Page 35                         March, 1992
  1936.  
  1937.  
  1938.  
  1939.           { Not quite a standard Boyer-Moore algorithm search routine }
  1940.           { To use:  pass buff as a dereferenced pointer to the buffer}
  1941.           {          st is the string being searched for              }
  1942.           {          size is the size of the buffer                   }
  1943.           { If st is not found, returns $ffff                         }
  1944.           var
  1945.             buffer : bigarray absolute buff;
  1946.             s      : array[0..255] of byte absolute st;
  1947.             len    : byte absolute st;
  1948.             s1     : string absolute st;
  1949.             s2     : string;
  1950.             count,
  1951.             x      : word;
  1952.             found  : boolean;
  1953.           begin
  1954.             s2[0] := chr(len);
  1955.             { sets the length to that of the search string }
  1956.             found := false;
  1957.             count := pred(len);
  1958.             while (not found) and (count < (size - len)) do begin
  1959.               if (buffer[count] = s[len]) then
  1960.               { there is a partial match } begin
  1961.                 if buffer[count-pred(len)] = s[1] then
  1962.                 { less partial! } begin
  1963.                   move(buffer[count-pred(len)],s2[1],len);
  1964.                   found := s1 = s2;
  1965.                   { if = it is a complete match }
  1966.                   BMSearch := count - pred(len);
  1967.                   { will stick unless not found }
  1968.                 end;
  1969.                 inc(count);    { bump by one char - match is irrelevant }
  1970.               end
  1971.               else
  1972.                 inc(count,Btable[buffer[count]]);
  1973.                 { no match so increment maximum }
  1974.             end;
  1975.             if not found then
  1976.               BMSearch := $ffff;
  1977.           end;  { BMSearch }
  1978.  
  1979.  
  1980.         begin
  1981.           new(buffer);
  1982.           assign(f,fname);
  1983.           reset(f,1);
  1984.           offset := 0;
  1985.           MakeBMTable(Btable,KeyStr);
  1986.           repeat
  1987.             BlockRead(f,buffer^,sizeof(buffer^),result);
  1988.             position := BMSearch(buffer^,KeyStr,result);
  1989.             finished := (result < sizeof(buffer^)) or (position <> $ffff);
  1990.             if position = $ffff then
  1991.               inc(offset,result);
  1992.             Strfound := position <> $ffff;
  1993.           until finished;
  1994.           close(f);
  1995.           if Strfound then
  1996.             writeln('Found at offset ',offset)
  1997.      PNL #10                      Page 36                         March, 1992
  1998.  
  1999.  
  2000.  
  2001.           else
  2002.             writeln('Not found');
  2003.         end.
  2004.  
  2005.      Q14. How can I put a apostrophe in a string?
  2006.  
  2007.      A14. Just put in extra apostrophes.   If you want st to be equal to
  2008.           the  string - The word 'quoted' is in quotes do this -
  2009.  
  2010.             st := 'The word ''quoted'' is in quotes';
  2011.  
  2012.           if you want the following to be written to screen -
  2013.              'This is a quoted string'
  2014.           do this -
  2015.  
  2016.             writeln('''This is a quoted string''');
  2017.  
  2018.  
  2019.      Q15. What are the best books to purchase to help me learn Turbo Pascal?
  2020.  
  2021.      A15. There are many good books for learners.  Here are a few -
  2022.  
  2023.           Complete Turbo Pascal - Third Edition - Jeff Duntemann
  2024.           Mastering Turbo Pascal 6 - Tom Swann
  2025.           Turbo Pascal - The Complete Reference - O'Brien.
  2026.  
  2027.           For  advanced  users  there are also many good books.   Here are a
  2028.      few
  2029.           that I have found  useful  -  (Those  marked  with an asterisk are
  2030.      not
  2031.           purely for Turbo Pascal)
  2032.  
  2033.           Turbo Pascal 6 - Techniques and Utilities - Rubenking
  2034.           Turbo Pascal Internals - Tischer
  2035.           * PC System Programming for Developers - Tischer
  2036.           * Undocumented DOS - Schulman
  2037.  
  2038.           Any learner would be well advised to obtain a well known library
  2039.      such
  2040.           as  Technojock's  Turbo Toolkit (TTT) which is shareware and study
  2041.      the
  2042.           source code.
  2043.  
  2044.      Trevor Carlsen
  2045.      (TeeCee)
  2046.      PNL #10                      Page 37                         March, 1992
  2047.  
  2048.  
  2049.  
  2050.      =================================================================
  2051.         Running 80286 Turbo Pascal Programmes on 8088/8086 Computers
  2052.      =================================================================
  2053.  
  2054.      [ Editor's note: Intentionally,  the author uses English spelling instead
  2055.      of American spelling ]
  2056.  
  2057.      Introduction
  2058.  
  2059.      When Borland released Turbo Pascal Version 6.0 in 1990,  they added  many
  2060.      impressive  features to the compiler which reaffirmed Turbo Pascal as the
  2061.      de facto standard Pascal  compiler  for  PCs.    One  such feature is the
  2062.      ability to generate iAPX286 instructions for added speed on AT or  higher
  2063.      machines;  unfortunately,  they forgot (neglected?) to add a check to the
  2064.      RTL (runtime library) for whether or not an iAPX286 was actually present.
  2065.      Any  programme  compiled  in the {$G+} state will undoubtedly "hang" when
  2066.      executed on an 8088 or 8086-based computer.
  2067.  
  2068.      In what  seems  an  afterthought,   Borland  added  a small demonstration
  2069.      programme called "TEST286" in  the  \DEMOS  subdirectory  of  your  Turbo
  2070.      Pascal  subtree,   and  wrote  a small mention in the "HELPME!.DOC" file.
  2071.      Following Borland's own advice, however:
  2072.  
  2073.           "If you want to put code like this in  a  program  with
  2074.           {$G+}  enabled,   put  the  test  and  halt code in the
  2075.           initialization section of  the  first  unit in the main
  2076.           program's USES clause."
  2077.  
  2078.      What follows is a  description  of  a  unit (provided) which does exactly
  2079.      that.  Place the unit's label immediately after the "uses" keyword before
  2080.      any other unit labels,   and  forget  about  it;   at  runtime  when  the
  2081.      initialisation code is executed and no 80286, 80386 or 80486 is detected,
  2082.      a message is displayed informing the user of the reason why the programme
  2083.      immediately thereafter exits (returning an errorlevel of one).
  2084.  
  2085.  
  2086.      Directives Overview
  2087.  
  2088.      This  section  merely  explains  the  logic  behind  each of the compiler
  2089.      directives used for the benefit of  novices,   and can be ignored by more
  2090.      competant programmers.
  2091.  
  2092.      As presented,  the unit is compiled in the {$G-} state to  ensure  proper
  2093.      execution on computers based on the 8088 or 8086 processor.   Further, it
  2094.      is  compiled with far calls and overlay ability enabled (viz.,  {$F+} and
  2095.      {$O+}) so upon completion in large applications,  the code can be swapped
  2096.      out (although this shouldn't be too  necessary as the entire unit is less
  2097.      than one kilobyte).
  2098.  
  2099.      The routine exhibited no problems on the test  machines  (an  80486,   an
  2100.      80386  AST  and  an  NEC  V20-based XT),  so has all debugging facilities
  2101.      disabled ({$D-,I-,L-,R-,S-}).   This should  not  prove  a problem in any
  2102.      programmes in which you have debugging enabled,  unless you wish to trace
  2103.      through the code.
  2104.  
  2105.      In addition to not including debugging  information,   the  "usual"  code
  2106.      optimisation  switches  are enabled ({$B-,V-}).   Since word-aligned data
  2107.      has no effect on the  8088,   yet  increases execution speed on all 80x86
  2108.      PNL #10                      Page 38                         March, 1992
  2109.  
  2110.  
  2111.  
  2112.      processors,  it is enabled ({$A+}).   There is no use  of  the  "extended
  2113.      syntax" option (again, a Version 6.0 enhancement), so it is disabled with
  2114.      {$X-}.
  2115.  
  2116.      The  addition  of  {$E-}  and  {$N-}  is  merely to "complete" the switch
  2117.      directives, and indicate that there is no need for any additional numeric
  2118.      or emulated floating point processing.
  2119.  
  2120.  
  2121.      Unit Description
  2122.  
  2123.      As there are no functions  or  procedures  to call (variables to read and
  2124.      alter,  constants to use,  et al.),  the "interface" section of the  unit
  2125.      remains void (empty).
  2126.  
  2127.      Both  the  errorlevel returned,  and the message displayed are defined as
  2128.      constants so that they  can  be  easily  altered  by an external setup or
  2129.      configuration utility.   The errorlevel can be any value from zero (0) to
  2130.      two hundred and fifty-five (255),  and the  error  message  can  be  upto
  2131.      eighty  (80)  characters  in length.   For users of Object Professional's
  2132.      OpClone unit, the string constant "i286 config data 1.00" is provided.
  2133.  
  2134.      The one internal variable  declared,   "Is286Able",   is used to pass the
  2135.      result of the detection routine to the remainder of the code;   this  was
  2136.      necessary as no separate function is used (to keep execution speed to the
  2137.      bare  minimum),  and standard functions and procedures (such as WriteLn()
  2138.      and Halt()) are not  callable  from  within the built-in inline assembler
  2139.      (one short-coming of BASM).
  2140.  
  2141.      The core of the original function provided in TEST286 is:
  2142.  
  2143.           asm
  2144.             pushf               { Push flags register onto the stack     }
  2145.             pop    bx           { Pop a word from the stack, store in BX }
  2146.             and    bx, 00FFFh   { Logical AND operands, result in BX     }
  2147.             push   bx           { Push word contents of BX on the stack  }
  2148.             popf                { Pop from stack into flags register     }
  2149.             pushf               { Push flags register onto the stack     }
  2150.             pop    bx           { Pop a word from the stack, store in BX }
  2151.             and    bx, 0F000h   { Logical AND operands, result in BX     }
  2152.             cmp    bx, 0F000h   { Compare operands, update status flags  }
  2153.             mov    ax, 00h      { Store word 0x0000 (zero) in AX         }
  2154.             jz     @@1          { If the zero flag is set, jump to "@@1" }
  2155.             mov    ax, 01h      { Store word 0x0001 (one) in AX          }
  2156.           @@1:                  { Internal label "@@1", end of routine   }
  2157.           end;
  2158.  
  2159.      The essential logic here is that for processors earlier  than  the  80286
  2160.      (viz.,  8088,  8086,  80188, 80186, V20 and V30) bits 12 to 15 of the CPU
  2161.      flags  register  cannot  be cleared.   This routine merely tries to clear
  2162.      those bits (without disturbing the others), and if it is unable to do so,
  2163.      assumes an iAPX286 or higher (i386, i486) is not present.
  2164.  
  2165.      For the supplied unit, the core is:
  2166.  
  2167.           asm
  2168.             xor    ah, ah       { Logical XOR operands, result in AH     }
  2169.             pushf               { Push flags register onto the stack     }
  2170.      PNL #10                      Page 39                         March, 1992
  2171.  
  2172.  
  2173.  
  2174.             pop    bx           { Pop a word from the stack, store in BX }
  2175.             and    bx, 00FFFh   { Logical AND operands, result in BX     }
  2176.             push   bx           { Push word contents of BX on the stack  }
  2177.             popf                { Pop from stack into flags register     }
  2178.             pushf               { Push flags register onto the stack     }
  2179.             pop    bx           { Pop a word from the stack, store in BX }
  2180.             and    bx, 0F000h   { Logical AND operands, result in BX     }
  2181.             cmp    bx, 0F000h   { Compare operands, update status flags  }
  2182.             je     @@1          { If the zero flag is set, jump to "@@1" }
  2183.             inc    ah           { Increment the contents of AH by one    }
  2184.           @@1:                  { Internal label "@@1", store result of  }
  2185.             mov    [Is286Able], ah   { detection in "Is286Able" variable }
  2186.           end;
  2187.  
  2188.      Logically,  the two code segments  are  identical (ignoring the last "mov
  2189.      [Is286Able], ah" instruction above).  If we compare their logic structure
  2190.      in English:
  2191.  
  2192.           (Original Code)
  2193.           1. Clear flag bits 12-15.
  2194.           2. Compare - did they clear?
  2195.           3. Set the result to zero (false).
  2196.           4. If the comparison in #3 resulted in the zero flag
  2197.              being set, ie., the bits did NOT clear, jump to
  2198.              the end of the routine.
  2199.           5. The bits DID clear, so set the result to one (true).
  2200.  
  2201.           (New Code)
  2202.           1. Set the result to zero (false).
  2203.           2. Clear flag bits 12-15.
  2204.           3. Compare - did they clear?
  2205.           4. If the comparison in #3 resulted in the zero flag
  2206.              being set, ie., the bits did NOT clear, jump to
  2207.              the end of the routine.
  2208.           5. The bits DID clear, so increment the result to one (true).
  2209.  
  2210.      Up to this point,  the comparison has been discussed as resulting in  the
  2211.      zero flag being set or cleared; to understand why, you must remember that
  2212.      comparisons are conducted by subtraction, so that if two items equal each
  2213.      other  numerically,  their difference is zero (hence the the zero flag is
  2214.      set).   To indicate more accurately  the  logic of the routine,  the "jz"
  2215.      (jump if zero set) instruction was replaced with the "je" (jump if  equal
  2216.      - ie., zero set) instruction.  Both are functionally identical.
  2217.  
  2218.      Saving Clock Cycles
  2219.  
  2220.      The differences remain now with two instructions:  "xor ah,  ah" and "inc
  2221.      ah".   Since only eight bits are needed for the result, AH is used rather
  2222.      than AX in its entirety.  The zeroing of the register with XOR instead of
  2223.      MOV ("xor ah,  ah" instead of "mov ah,  0") saves one clock cycle on 8086
  2224.      processors  (none  on  80286,  80386 or 80486 processors) and works since
  2225.      performing a logical exclusive OR (as  opposed to a logical inclusive OR)
  2226.      on a number with itself always results in zero.
  2227.  
  2228.      Moving the placement of  the  "set-result-to-false"  instruction  to  the
  2229.      start not only makes more sense,  but is necessary as the XOR instruction
  2230.      modifies  the zero flag (which then could not be used in the ensuing jump
  2231.      instruction).
  2232.      PNL #10                      Page 40                         March, 1992
  2233.  
  2234.  
  2235.  
  2236.  
  2237.      Rather than loading AH with  one  ("mov  ah,  1" indicating "true"),  the
  2238.      increment instruction is used ("inc ah") as,  with XOR, it saves an extra
  2239.      valuable clock cycle on 8086 processors.   Admittedly, if the host CPU is
  2240.      an 8086 the increment instruction is never reached,  but it does not hurt
  2241.      to optimise at the instruction level.
  2242.  
  2243.  
  2244.      Remaining Code
  2245.  
  2246.      The last five lines of the routine proper are  in  Pascal  (as  explained
  2247.      above,   BASM  doesn't  allow calling standard procedures and functions).
  2248.      These merely check the status  of  the  "Is286Able" variable,  and if the
  2249.      assembly code set it to "false", print a message before exiting.
  2250.  
  2251.  
  2252.      Test Programme
  2253.  
  2254.      A driver programme to test the unit is simply:
  2255.  
  2256.           program Testi286;
  2257.           {$G+}
  2258.  
  2259.           uses
  2260.             i286;
  2261.  
  2262.            {-If a '286 is absent, the init code of the unit will exit}
  2263.           begin
  2264.             WriteLn('Obviously an 80286 or higher is in this machine.')
  2265.           end.   { Testi286 }
  2266.  
  2267.  
  2268.      Conclusion
  2269.  
  2270.      While many will claim the saving of two (well,  one) clock cycles  is  no
  2271.      more  than academic,  this was not the main aim of the unit.   The "i286"
  2272.      unit provides a very easy to use, "plug 'n' play" method of detecting and
  2273.      exiting which requires no further effort on the part of the programmer.
  2274.  
  2275.      David J. N. Begley
  2276.      58:2100/142@intlnet, 3:712/211.3@fidonet
  2277.      Department of Computing, Faculty of Science and Technology
  2278.      University of Western Sydney, Nepean
  2279.      PNL #10                      Page 41                         March, 1992
  2280.  
  2281.  
  2282.  
  2283.      =================================================================
  2284.                               Conclusion
  2285.      =================================================================
  2286.  
  2287.      That's it for now.
  2288.  
  2289.      In the next issue,  I will publish the first part of  what  I  call  the
  2290.      Beginner's  Toolbox.   It is a set of units to help beginners to program
  2291.      easily.  It will include input/output routines, a help system, a menuing
  2292.      system, etc...
  2293.  
  2294.      I thank everybody  who  participated  in  the  rebirth of PNL.   Richard
  2295.      Morris,  the editor over-the-pond has collected  most  of  the  articles
  2296.      here.   Without him, there would have been only 2 articles here by March
  2297.      1st, 1992.
  2298.  
  2299.      Again,   I  beg you all to send in articles.   Everybody has his/her own
  2300.      techniques to program,  comments to share with others.   You can send in
  2301.      book reviews or software reviews,
  2302.  
  2303.      The newsletter depends on your contribution...
  2304.  
  2305.      Anybody interested in  publishing  an  article  can  request the Article
  2306.      Specifications with the magic name ARTSPEC on  FidoNet  1:167/405  -  It
  2307.      will also be available soon from Richard Morris, FidoNet 3:640/372.5.
  2308.  
  2309.      If you would like to receive back issues of PNL directly from me, send a
  2310.      diskette and $2.00 for shipping.  Don't forget to include your address.
  2311.      
  2312.                     Send your order to:
  2313.                        Alex Boisvert
  2314.                        86 Bryant St.
  2315.                        Sherbrooke, Quebec
  2316.                        Canada   J1J 3E4
  2317.                        
  2318.      If  you are a SysOp that will regularly carry PNL and would like to have
  2319.      your bulletin board listed as such,   here,  send me a message either by
  2320.      postal mail or at one of the electronic addresses  given  on  the  title
  2321.      page, with your bulletin board's name, phone number, and your name.
  2322.  
  2323.                              Distribution List
  2324.  
  2325.      The  following  is  the  phone numbers to bulletin boards known to carry
  2326.      PNL.   If you would like your  bulletin board's name and number added to
  2327.      or deleted from this list,  please send me a message at one of  my  many
  2328.      addresses.    I  can  not guarantee whether a listed board will have any
  2329.      particular issue, however.
  2330.  
  2331.         Thieve's World ......................... Phone: (713) 463-8053
  2332.         Hippocampus ............................ Phone: (203) 484-4621
  2333.         Turbo City BBS ......................... Phone: (209) 599-7435
  2334.         The Final Frontier BBS.................. Phone: (518) 761-0869
  2335.